summaryrefslogtreecommitdiff
path: root/app/assets/javascripts/projects
diff options
context:
space:
mode:
Diffstat (limited to 'app/assets/javascripts/projects')
-rw-r--r--app/assets/javascripts/projects/compare/components/app.vue20
-rw-r--r--app/assets/javascripts/projects/compare/index.js12
-rw-r--r--app/assets/javascripts/projects/pipelines/charts/index.js2
-rw-r--r--app/assets/javascripts/projects/project_new.js24
-rw-r--r--app/assets/javascripts/projects/settings/branch_rules/components/branch_dropdown.vue87
-rw-r--r--app/assets/javascripts/projects/settings/branch_rules/components/protections/index.vue59
-rw-r--r--app/assets/javascripts/projects/settings/branch_rules/components/protections/merge_protections.vue46
-rw-r--r--app/assets/javascripts/projects/settings/branch_rules/components/protections/push_protections.vue52
-rw-r--r--app/assets/javascripts/projects/settings/branch_rules/components/rule_edit.vue44
-rw-r--r--app/assets/javascripts/projects/settings/components/access_dropdown.vue4
-rw-r--r--app/assets/javascripts/projects/settings/components/transfer_project_form.vue94
-rw-r--r--app/assets/javascripts/projects/settings/graphql/queries/search_namespaces_where_user_can_transfer_projects.query.graphql24
-rw-r--r--app/assets/javascripts/projects/settings/init_transfer_project_form.js22
13 files changed, 409 insertions, 81 deletions
diff --git a/app/assets/javascripts/projects/compare/components/app.vue b/app/assets/javascripts/projects/compare/components/app.vue
index bda58091b97..4ba7156b026 100644
--- a/app/assets/javascripts/projects/compare/components/app.vue
+++ b/app/assets/javascripts/projects/compare/components/app.vue
@@ -15,7 +15,11 @@ export default {
type: String,
required: true,
},
- refsProjectPath: {
+ sourceProjectRefsPath: {
+ type: String,
+ required: true,
+ },
+ targetProjectRefsPath: {
type: String,
required: true,
},
@@ -37,7 +41,11 @@ export default {
type: String,
required: true,
},
- defaultProject: {
+ sourceProject: {
+ type: Object,
+ required: true,
+ },
+ targetProject: {
type: Object,
required: true,
},
@@ -50,14 +58,14 @@ export default {
return {
from: {
projects: this.projects,
- selectedProject: this.defaultProject,
+ selectedProject: this.targetProject,
revision: this.paramsFrom,
- refsProjectPath: this.refsProjectPath,
+ refsProjectPath: this.targetProjectRefsPath,
},
to: {
- selectedProject: this.defaultProject,
+ selectedProject: this.sourceProject,
revision: this.paramsTo,
- refsProjectPath: this.refsProjectPath,
+ refsProjectPath: this.sourceProjectRefsPath,
},
};
},
diff --git a/app/assets/javascripts/projects/compare/index.js b/app/assets/javascripts/projects/compare/index.js
index e485a086d39..074b8565c3c 100644
--- a/app/assets/javascripts/projects/compare/index.js
+++ b/app/assets/javascripts/projects/compare/index.js
@@ -5,13 +5,15 @@ export default function init() {
const el = document.getElementById('js-compare-selector');
const {
- refsProjectPath,
+ sourceProjectRefsPath,
+ targetProjectRefsPath,
paramsFrom,
paramsTo,
projectCompareIndexPath,
projectMergeRequestPath,
createMrPath,
- projectTo,
+ sourceProject,
+ targetProject,
projectsFrom,
} = el.dataset;
@@ -23,13 +25,15 @@ export default function init() {
render(createElement) {
return createElement(CompareApp, {
props: {
- refsProjectPath,
+ sourceProjectRefsPath,
+ targetProjectRefsPath,
paramsFrom,
paramsTo,
projectCompareIndexPath,
projectMergeRequestPath,
createMrPath,
- defaultProject: JSON.parse(projectTo),
+ sourceProject: JSON.parse(sourceProject),
+ targetProject: JSON.parse(targetProject),
projects: JSON.parse(projectsFrom),
},
});
diff --git a/app/assets/javascripts/projects/pipelines/charts/index.js b/app/assets/javascripts/projects/pipelines/charts/index.js
index 28b77f6defd..0cfea401be6 100644
--- a/app/assets/javascripts/projects/pipelines/charts/index.js
+++ b/app/assets/javascripts/projects/pipelines/charts/index.js
@@ -17,6 +17,7 @@ const mountPipelineChartsApp = (el) => {
coverageChartPath,
defaultBranch,
testRunsEmptyStateImagePath,
+ projectQualitySummaryFeedbackImagePath,
} = el.dataset;
const shouldRenderDoraCharts = parseBoolean(el.dataset.shouldRenderDoraCharts);
@@ -37,6 +38,7 @@ const mountPipelineChartsApp = (el) => {
coverageChartPath,
defaultBranch,
testRunsEmptyStateImagePath,
+ projectQualitySummaryFeedbackImagePath,
},
render: (createElement) => createElement(ProjectPipelinesCharts, {}),
});
diff --git a/app/assets/javascripts/projects/project_new.js b/app/assets/javascripts/projects/project_new.js
index fe84660422b..424ea3b61c5 100644
--- a/app/assets/javascripts/projects/project_new.js
+++ b/app/assets/javascripts/projects/project_new.js
@@ -2,6 +2,7 @@ import $ from 'jquery';
import { debounce } from 'lodash';
import DEFAULT_PROJECT_TEMPLATES from 'any_else_ce/projects/default_project_templates';
import { confirmAction } from '~/lib/utils/confirm_via_gl_modal/confirm_via_gl_modal';
+import Tracking from '~/tracking';
import { DEFAULT_DEBOUNCE_AND_THROTTLE_MS } from '../lib/utils/constants';
import { ENTER_KEY } from '../lib/utils/keys';
import axios from '../lib/utils/axios_utils';
@@ -109,8 +110,31 @@ const setProjectNamePathHandlers = ($projectNameInput, $projectPathInput) => {
);
};
+ const projectPathValueListener = () => {
+ // eslint-disable-next-line no-param-reassign
+ $projectPathInput.oldInputValue = $projectPathInput.value;
+ };
+
+ const projectPathTrackListener = () => {
+ if ($projectPathInput.oldInputValue === $projectPathInput.value) {
+ // no change made to the input
+ return;
+ }
+
+ const trackEvent = 'user_input_path_slug';
+ const trackCategory = undefined; // will be default set in event method
+
+ Tracking.event(trackCategory, trackEvent, {
+ label: 'new_project_form',
+ });
+ };
+
$projectPathInput.removeEventListener('keyup', projectPathInputListener);
$projectPathInput.addEventListener('keyup', projectPathInputListener);
+ $projectPathInput.removeEventListener('focus', projectPathValueListener);
+ $projectPathInput.addEventListener('focus', projectPathValueListener);
+ $projectPathInput.removeEventListener('blur', projectPathTrackListener);
+ $projectPathInput.addEventListener('blur', projectPathTrackListener);
$projectPathInput.removeEventListener('change', projectPathInputListener);
$projectPathInput.addEventListener('change', projectPathInputListener);
diff --git a/app/assets/javascripts/projects/settings/branch_rules/components/branch_dropdown.vue b/app/assets/javascripts/projects/settings/branch_rules/components/branch_dropdown.vue
index 6bbe0ab7d5f..6ba2ef7da99 100644
--- a/app/assets/javascripts/projects/settings/branch_rules/components/branch_dropdown.vue
+++ b/app/assets/javascripts/projects/settings/branch_rules/components/branch_dropdown.vue
@@ -1,12 +1,24 @@
<script>
-import { GlDropdown, GlDropdownItem, GlDropdownDivider, GlSearchBoxByType } from '@gitlab/ui';
+import {
+ GlDropdown,
+ GlDropdownItem,
+ GlDropdownDivider,
+ GlSearchBoxByType,
+ GlSprintf,
+ GlLink,
+} from '@gitlab/ui';
import { createAlert } from '~/flash';
-import { __, sprintf } from '~/locale';
+import { s__, sprintf } from '~/locale';
+import { helpPagePath } from '~/helpers/help_page_helper';
import branchesQuery from '../queries/branches.query.graphql';
export const i18n = {
- fetchBranchesError: __('An error occurred while fetching branches.'),
- noMatch: __('No matching results'),
+ fetchBranchesError: s__('BranchRules|An error occurred while fetching branches.'),
+ noMatch: s__('BranchRules|No matching results'),
+ branchHelpText: s__(
+ 'BranchRules|%{linkStart}Wildcards%{linkEnd} such as *-stable or production/* are supported.',
+ ),
+ wildCardSearchHelp: s__('BranchRules|Create wildcard: %{searchTerm}'),
};
export default {
@@ -17,6 +29,8 @@ export default {
GlDropdownItem,
GlDropdownDivider,
GlSearchBoxByType,
+ GlSprintf,
+ GlLink,
},
apollo: {
branchNames: {
@@ -39,6 +53,10 @@ export default {
},
},
},
+ searchInputDelay: 250,
+ wildcardsHelpPath: helpPagePath('user/project/protected_branches', {
+ anchor: 'configure-multiple-protected-branches-by-using-a-wildcard',
+ }),
props: {
projectPath: {
type: String,
@@ -58,7 +76,9 @@ export default {
},
computed: {
createButtonLabel() {
- return sprintf(__('Create wildcard: %{searchTerm}'), { searchTerm: this.searchTerm });
+ return sprintf(this.$options.i18n.wildCardSearchHelp, {
+ searchTerm: this.searchTerm,
+ });
},
shouldRenderCreateButton() {
return this.searchTerm && !this.branchNames.includes(this.searchTerm);
@@ -81,30 +101,37 @@ export default {
};
</script>
<template>
- <gl-dropdown :text="value || branchNames[0]">
- <gl-search-box-by-type
- v-model.trim="searchTerm"
- data-testid="branch-search"
- debounce="250"
- :is-loading="isLoading"
- />
- <gl-dropdown-item
- v-for="branch in branchNames"
- :key="branch"
- :is-checked="isSelected(branch)"
- is-check-item
- @click="selectBranch(branch)"
- >
- {{ branch }}
- </gl-dropdown-item>
- <gl-dropdown-item v-if="!branchNames.length && !isLoading" data-testid="no-data">{{
- $options.i18n.noMatch
- }}</gl-dropdown-item>
- <template v-if="shouldRenderCreateButton">
- <gl-dropdown-divider />
- <gl-dropdown-item data-testid="create-wildcard-button" @click="createWildcard">
- {{ createButtonLabel }}
+ <div>
+ <gl-dropdown :text="value || branchNames[0]" class="gl-w-full">
+ <gl-search-box-by-type
+ v-model.trim="searchTerm"
+ data-testid="branch-search"
+ :debounce="$options.searchInputDelay"
+ :is-loading="isLoading"
+ />
+ <gl-dropdown-item
+ v-for="branch in branchNames"
+ :key="branch"
+ :is-checked="isSelected(branch)"
+ is-check-item
+ @click="selectBranch(branch)"
+ >
+ {{ branch }}
</gl-dropdown-item>
- </template>
- </gl-dropdown>
+ <gl-dropdown-item v-if="!branchNames.length && !isLoading" data-testid="no-data">{{
+ $options.i18n.noMatch
+ }}</gl-dropdown-item>
+ <template v-if="shouldRenderCreateButton">
+ <gl-dropdown-divider />
+ <gl-dropdown-item data-testid="create-wildcard-button" @click="createWildcard">
+ {{ createButtonLabel }}
+ </gl-dropdown-item>
+ </template>
+ </gl-dropdown>
+ <gl-sprintf :message="$options.i18n.branchHelpText">
+ <template #link="{ content }">
+ <gl-link :href="$options.wildcardsHelpPath">{{ content }}</gl-link>
+ </template>
+ </gl-sprintf>
+ </div>
</template>
diff --git a/app/assets/javascripts/projects/settings/branch_rules/components/protections/index.vue b/app/assets/javascripts/projects/settings/branch_rules/components/protections/index.vue
new file mode 100644
index 00000000000..bcc0f64d667
--- /dev/null
+++ b/app/assets/javascripts/projects/settings/branch_rules/components/protections/index.vue
@@ -0,0 +1,59 @@
+<script>
+import { GlSprintf, GlLink } from '@gitlab/ui';
+import { s__ } from '~/locale';
+import { helpPagePath } from '~/helpers/help_page_helper';
+import PushProtections from './push_protections.vue';
+import MergeProtections from './merge_protections.vue';
+
+export const i18n = {
+ protections: s__('BranchRules|Protections'),
+ protectionsHelpText: s__(
+ 'BranchRules|Keep stable branches secure and force developers to use merge requests. %{linkStart}What are protected branches?%{linkEnd}',
+ ),
+};
+
+export default {
+ name: 'BranchProtections',
+ i18n,
+ components: {
+ GlSprintf,
+ GlLink,
+ PushProtections,
+ MergeProtections,
+ },
+ protectedBranchesHelpPath: helpPagePath('user/project/protected_branches'),
+ props: {
+ protections: {
+ type: Object,
+ required: true,
+ },
+ },
+};
+</script>
+
+<template>
+ <div>
+ <h4 class="gl-border-t gl-pt-4">{{ $options.i18n.protections }}</h4>
+
+ <div data-testid="protections-help-text">
+ <gl-sprintf :message="$options.i18n.protectionsHelpText">
+ <template #link="{ content }">
+ <gl-link :href="$options.protectedBranchesHelpPath">{{ content }}</gl-link>
+ </template>
+ </gl-sprintf>
+ </div>
+
+ <push-protections
+ class="gl-mt-5"
+ :members-allowed-to-push="protections.membersAllowedToPush"
+ :allow-force-push="protections.allowForcePush"
+ v-on="$listeners"
+ />
+
+ <merge-protections
+ :members-allowed-to-merge="protections.membersAllowedToMerge"
+ :require-code-owners-approval="protections.requireCodeOwnersApproval"
+ v-on="$listeners"
+ />
+ </div>
+</template>
diff --git a/app/assets/javascripts/projects/settings/branch_rules/components/protections/merge_protections.vue b/app/assets/javascripts/projects/settings/branch_rules/components/protections/merge_protections.vue
new file mode 100644
index 00000000000..85f168af4a8
--- /dev/null
+++ b/app/assets/javascripts/projects/settings/branch_rules/components/protections/merge_protections.vue
@@ -0,0 +1,46 @@
+<script>
+import { GlFormGroup, GlFormCheckbox } from '@gitlab/ui';
+import { s__ } from '~/locale';
+
+export const i18n = {
+ allowedToMerge: s__('BranchRules|Allowed to merge'),
+ requireApprovalTitle: s__('BranchRules|Require approval from code owners.'),
+ requireApprovalHelpText: s__(
+ 'BranchRules|Reject code pushes that change files listed in the CODEOWNERS file.',
+ ),
+};
+
+export default {
+ name: 'BranchMergeProtections',
+ i18n,
+ components: {
+ GlFormGroup,
+ GlFormCheckbox,
+ },
+ props: {
+ membersAllowedToMerge: {
+ type: Array,
+ required: true,
+ },
+ requireCodeOwnersApproval: {
+ type: Boolean,
+ required: true,
+ },
+ },
+};
+</script>
+
+<template>
+ <gl-form-group :label="$options.i18n.allowedToMerge">
+ <!-- TODO: add multi-select-dropdown (https://gitlab.com/gitlab-org/gitlab/-/issues/362212) -->
+
+ <gl-form-checkbox
+ class="gl-mt-5"
+ :checked="requireCodeOwnersApproval"
+ @change="$emit('change-require-code-owners-approval', $event)"
+ >
+ <span>{{ $options.i18n.requireApprovalTitle }}</span>
+ <template #help>{{ $options.i18n.requireApprovalHelpText }}</template>
+ </gl-form-checkbox>
+ </gl-form-group>
+</template>
diff --git a/app/assets/javascripts/projects/settings/branch_rules/components/protections/push_protections.vue b/app/assets/javascripts/projects/settings/branch_rules/components/protections/push_protections.vue
new file mode 100644
index 00000000000..541923bb735
--- /dev/null
+++ b/app/assets/javascripts/projects/settings/branch_rules/components/protections/push_protections.vue
@@ -0,0 +1,52 @@
+<script>
+import { GlFormGroup, GlSprintf, GlLink, GlFormCheckbox } from '@gitlab/ui';
+import { s__ } from '~/locale';
+import { helpPagePath } from '~/helpers/help_page_helper';
+
+export const i18n = {
+ allowedToPush: s__('BranchRules|Allowed to push'),
+ forcePushTitle: s__(
+ 'BranchRules|Allow all users with push access to %{linkStart}force push%{linkEnd}.',
+ ),
+};
+
+export default {
+ name: 'BranchPushProtections',
+ i18n,
+ components: {
+ GlFormGroup,
+ GlSprintf,
+ GlLink,
+ GlFormCheckbox,
+ },
+ forcePushHelpPath: helpPagePath('topics/git/git_rebase', { anchor: 'force-push' }),
+ props: {
+ membersAllowedToPush: {
+ type: Array,
+ required: true,
+ },
+ allowForcePush: {
+ type: Boolean,
+ required: true,
+ },
+ },
+};
+</script>
+
+<template>
+ <gl-form-group :label="$options.i18n.allowedToPush">
+ <!-- TODO: add multi-select-dropdown (https://gitlab.com/gitlab-org/gitlab/-/issues/362212) -->
+
+ <gl-form-checkbox
+ class="gl-mt-5"
+ :checked="allowForcePush"
+ @change="$emit('change-allow-force-push', $event)"
+ >
+ <gl-sprintf :message="$options.i18n.forcePushTitle">
+ <template #link="{ content }">
+ <gl-link :href="$options.forcePushHelpPath">{{ content }}</gl-link>
+ </template>
+ </gl-sprintf>
+ </gl-form-checkbox>
+ </gl-form-group>
+</template>
diff --git a/app/assets/javascripts/projects/settings/branch_rules/components/rule_edit.vue b/app/assets/javascripts/projects/settings/branch_rules/components/rule_edit.vue
index c2e7f4e9b1b..ad3eb7d2899 100644
--- a/app/assets/javascripts/projects/settings/branch_rules/components/rule_edit.vue
+++ b/app/assets/javascripts/projects/settings/branch_rules/components/rule_edit.vue
@@ -1,15 +1,18 @@
<script>
import { GlFormGroup } from '@gitlab/ui';
-import { __ } from '~/locale';
+import { s__ } from '~/locale';
import { getParameterByName } from '~/lib/utils/url_utility';
import BranchDropdown from './branch_dropdown.vue';
+import Protections from './protections/index.vue';
export default {
name: 'RuleEdit',
- i18n: {
- branch: __('Branch'),
+ i18n: { branch: s__('BranchRules|Branch') },
+ components: {
+ BranchDropdown,
+ GlFormGroup,
+ Protections,
},
- components: { BranchDropdown, GlFormGroup },
props: {
projectPath: {
type: String,
@@ -19,20 +22,35 @@ export default {
data() {
return {
branch: getParameterByName('branch'),
+ protections: {
+ membersAllowedToPush: [],
+ allowForcePush: false,
+ membersAllowedToMerge: [],
+ requireCodeOwnersApproval: false,
+ },
};
},
};
</script>
<template>
- <gl-form-group :label="$options.i18n.branch">
- <branch-dropdown
- id="branches"
- v-model="branch"
- class="gl-w-half"
- :project-path="projectPath"
- @createWildcard="branch = $event"
+ <div>
+ <gl-form-group :label="$options.i18n.branch">
+ <branch-dropdown
+ id="branches"
+ v-model="branch"
+ class="gl-w-half"
+ :project-path="projectPath"
+ @createWildcard="branch = $event"
+ />
+ </gl-form-group>
+
+ <protections
+ :protections="protections"
+ @change-allowed-to-push-members="protections.membersAllowedToPush = $event"
+ @change-allow-force-push="protections.allowForcePush = $event"
+ @change-allowed-to-merge-members="protections.membersAllowedToMerge = $event"
+ @change-require-code-owners-approval="protections.requireCodeOwnersApproval = $event"
/>
- </gl-form-group>
- <!-- TODO - Add branch protections (https://gitlab.com/gitlab-org/gitlab/-/issues/362212) -->
+ </div>
</template>
diff --git a/app/assets/javascripts/projects/settings/components/access_dropdown.vue b/app/assets/javascripts/projects/settings/components/access_dropdown.vue
index fcf81c9d1f7..2209172c06d 100644
--- a/app/assets/javascripts/projects/settings/components/access_dropdown.vue
+++ b/app/assets/javascripts/projects/settings/components/access_dropdown.vue
@@ -262,8 +262,8 @@ export default {
const selectedUsers = this.preselectedItems
.filter(({ type }) => type === LEVEL_TYPES.USER)
- .map(({ user_id, name, username, avatar_url, type }) => ({
- id: user_id,
+ .map(({ user_id: id, name, username, avatar_url, type }) => ({
+ id,
name,
username,
avatar_url,
diff --git a/app/assets/javascripts/projects/settings/components/transfer_project_form.vue b/app/assets/javascripts/projects/settings/components/transfer_project_form.vue
index fe968e74c6d..c13753da00b 100644
--- a/app/assets/javascripts/projects/settings/components/transfer_project_form.vue
+++ b/app/assets/javascripts/projects/settings/components/transfer_project_form.vue
@@ -1,7 +1,13 @@
<script>
import { GlFormGroup } from '@gitlab/ui';
-import NamespaceSelect from '~/vue_shared/components/namespace_select/namespace_select.vue';
+import produce from 'immer';
import ConfirmDanger from '~/vue_shared/components/confirm_danger/confirm_danger.vue';
+import NamespaceSelect from '~/vue_shared/components/namespace_select/namespace_select.vue';
+import { getIdFromGraphQLId } from '~/graphql_shared/utils';
+import { DEBOUNCE_DELAY } from '~/vue_shared/components/filtered_search_bar/constants';
+import searchNamespacesWhereUserCanTransferProjects from '../graphql/queries/search_namespaces_where_user_can_transfer_projects.query.graphql';
+
+const GROUPS_PER_PAGE = 25;
export default {
name: 'TransferProjectForm',
@@ -11,14 +17,6 @@ export default {
ConfirmDanger,
},
props: {
- groupNamespaces: {
- type: Array,
- required: true,
- },
- userNamespaces: {
- type: Array,
- required: true,
- },
confirmationPhrase: {
type: String,
required: true,
@@ -28,19 +26,88 @@ export default {
required: true,
},
},
+ apollo: {
+ currentUser: {
+ query: searchNamespacesWhereUserCanTransferProjects,
+ debounce: DEBOUNCE_DELAY,
+ variables() {
+ return {
+ search: this.searchTerm,
+ after: null,
+ first: GROUPS_PER_PAGE,
+ };
+ },
+ result() {
+ this.isLoadingMoreGroups = false;
+ this.isSearchLoading = false;
+ },
+ },
+ },
data() {
- return { selectedNamespace: null };
+ return {
+ currentUser: {},
+ selectedNamespace: null,
+ isLoadingMoreGroups: false,
+ isSearchLoading: false,
+ searchTerm: '',
+ };
},
computed: {
hasSelectedNamespace() {
return Boolean(this.selectedNamespace?.id);
},
+ groupNamespaces() {
+ return this.currentUser.groups?.nodes?.map(this.formatNamespace) || [];
+ },
+ userNamespaces() {
+ const { namespace } = this.currentUser;
+
+ return namespace ? [this.formatNamespace(namespace)] : [];
+ },
+ hasNextPageOfGroups() {
+ return this.currentUser.groups?.pageInfo?.hasNextPage || false;
+ },
},
methods: {
handleSelect(selectedNamespace) {
this.selectedNamespace = selectedNamespace;
this.$emit('selectNamespace', selectedNamespace.id);
},
+ handleLoadMoreGroups() {
+ this.isLoadingMoreGroups = true;
+
+ this.$apollo.queries.currentUser.fetchMore({
+ variables: {
+ after: this.currentUser.groups.pageInfo.endCursor,
+ first: GROUPS_PER_PAGE,
+ },
+ updateQuery(
+ previousResult,
+ {
+ fetchMoreResult: {
+ currentUser: { groups: newGroups },
+ },
+ },
+ ) {
+ const previousGroups = previousResult.currentUser.groups;
+
+ return produce(previousResult, (draftData) => {
+ draftData.currentUser.groups.nodes = [...previousGroups.nodes, ...newGroups.nodes];
+ draftData.currentUser.groups.pageInfo = newGroups.pageInfo;
+ });
+ },
+ });
+ },
+ handleSearch(searchTerm) {
+ this.isSearchLoading = true;
+ this.searchTerm = searchTerm;
+ },
+ formatNamespace({ id, fullName }) {
+ return {
+ id: getIdFromGraphQLId(id),
+ humanName: fullName,
+ };
+ },
},
};
</script>
@@ -53,11 +120,16 @@ export default {
:group-namespaces="groupNamespaces"
:user-namespaces="userNamespaces"
:selected-namespace="selectedNamespace"
+ :has-next-page-of-groups="hasNextPageOfGroups"
+ :is-loading-more-groups="isLoadingMoreGroups"
+ :is-search-loading="isSearchLoading"
+ :should-filter-namespaces="false"
@select="handleSelect"
+ @load-more-groups="handleLoadMoreGroups"
+ @search="handleSearch"
/>
</gl-form-group>
<confirm-danger
- button-class="qa-transfer-button"
:disabled="!hasSelectedNamespace"
:phrase="confirmationPhrase"
:button-text="confirmButtonText"
diff --git a/app/assets/javascripts/projects/settings/graphql/queries/search_namespaces_where_user_can_transfer_projects.query.graphql b/app/assets/javascripts/projects/settings/graphql/queries/search_namespaces_where_user_can_transfer_projects.query.graphql
new file mode 100644
index 00000000000..d4bcb8c869c
--- /dev/null
+++ b/app/assets/javascripts/projects/settings/graphql/queries/search_namespaces_where_user_can_transfer_projects.query.graphql
@@ -0,0 +1,24 @@
+#import "~/graphql_shared/fragments/page_info.fragment.graphql"
+
+query searchNamespacesWhereUserCanTransferProjects(
+ $search: String = ""
+ $after: String = ""
+ $first: Int = null
+) {
+ currentUser {
+ id
+ groups(permissionScope: TRANSFER_PROJECTS, search: $search, after: $after, first: $first) {
+ nodes {
+ id
+ fullName
+ }
+ pageInfo {
+ ...PageInfo
+ }
+ }
+ namespace {
+ id
+ fullName
+ }
+ }
+}
diff --git a/app/assets/javascripts/projects/settings/init_transfer_project_form.js b/app/assets/javascripts/projects/settings/init_transfer_project_form.js
index a5f720bffaa..bc1aff640d2 100644
--- a/app/assets/javascripts/projects/settings/init_transfer_project_form.js
+++ b/app/assets/javascripts/projects/settings/init_transfer_project_form.js
@@ -1,36 +1,29 @@
import Vue from 'vue';
-import { convertObjectPropsToCamelCase } from '~/lib/utils/common_utils';
+import VueApollo from 'vue-apollo';
+import createDefaultClient from '~/lib/graphql';
import TransferProjectForm from './components/transfer_project_form.vue';
-const prepareNamespaces = (rawNamespaces = '') => {
- if (!rawNamespaces) {
- return { groupNamespaces: [], userNamespaces: [] };
- }
-
- const data = JSON.parse(rawNamespaces);
- return {
- groupNamespaces: data?.group?.map(convertObjectPropsToCamelCase) || [],
- userNamespaces: data?.user?.map(convertObjectPropsToCamelCase) || [],
- };
-};
-
export default () => {
const el = document.querySelector('.js-transfer-project-form');
if (!el) {
return false;
}
+ Vue.use(VueApollo);
+
const {
targetFormId = null,
targetHiddenInputId = null,
buttonText: confirmButtonText = '',
phrase: confirmationPhrase = '',
confirmDangerMessage = '',
- namespaces = '',
} = el.dataset;
return new Vue({
el,
+ apolloProvider: new VueApollo({
+ defaultClient: createDefaultClient(),
+ }),
provide: {
confirmDangerMessage,
},
@@ -39,7 +32,6 @@ export default () => {
props: {
confirmButtonText,
confirmationPhrase,
- ...prepareNamespaces(namespaces),
},
on: {
selectNamespace: (id) => {