diff options
author | GitLab Bot <gitlab-bot@gitlab.com> | 2022-11-22 15:07:45 +0300 |
---|---|---|
committer | GitLab Bot <gitlab-bot@gitlab.com> | 2022-11-22 15:07:45 +0300 |
commit | 25bf8a36335fa281dc8fe56f355f3829ace703a5 (patch) | |
tree | 33731aef63f86469a57185a8dfb46071007ed61c /app/assets/javascripts/sidebar | |
parent | 02a0289d4077552778c0eeb0cc0172dfd7b6b331 (diff) |
Add latest changes from gitlab-org/gitlab@master
Diffstat (limited to 'app/assets/javascripts/sidebar')
-rw-r--r-- | app/assets/javascripts/sidebar/components/copy_email/copy_email_to_clipboard.vue (renamed from app/assets/javascripts/sidebar/components/copy_email_to_clipboard.vue) | 0 | ||||
-rw-r--r-- | app/assets/javascripts/sidebar/components/move/move_issues_button.vue | 171 | ||||
-rw-r--r-- | app/assets/javascripts/sidebar/components/status/status_dropdown.vue | 57 | ||||
-rw-r--r-- | app/assets/javascripts/sidebar/components/subscriptions/subscriptions_dropdown.vue | 51 | ||||
-rw-r--r-- | app/assets/javascripts/sidebar/constants.js | 22 | ||||
-rw-r--r-- | app/assets/javascripts/sidebar/mount_sidebar.js | 83 | ||||
-rw-r--r-- | app/assets/javascripts/sidebar/queries/move_issue.mutation.graphql | 5 |
7 files changed, 376 insertions, 13 deletions
diff --git a/app/assets/javascripts/sidebar/components/copy_email_to_clipboard.vue b/app/assets/javascripts/sidebar/components/copy_email/copy_email_to_clipboard.vue index fd652583f76..fd652583f76 100644 --- a/app/assets/javascripts/sidebar/components/copy_email_to_clipboard.vue +++ b/app/assets/javascripts/sidebar/components/copy_email/copy_email_to_clipboard.vue diff --git a/app/assets/javascripts/sidebar/components/move/move_issues_button.vue b/app/assets/javascripts/sidebar/components/move/move_issues_button.vue new file mode 100644 index 00000000000..a3a5072fc2d --- /dev/null +++ b/app/assets/javascripts/sidebar/components/move/move_issues_button.vue @@ -0,0 +1,171 @@ +<script> +import { GlAlert } from '@gitlab/ui'; +import IssuableMoveDropdown from '~/vue_shared/components/sidebar/issuable_move_dropdown.vue'; +import createFlash from '~/flash'; +import { logError } from '~/lib/logger'; +import { s__ } from '~/locale'; +import { + WORK_ITEM_TYPE_ENUM_ISSUE, + WORK_ITEM_TYPE_ENUM_INCIDENT, + WORK_ITEM_TYPE_ENUM_TASK, + WORK_ITEM_TYPE_ENUM_TEST_CASE, +} from '~/work_items/constants'; +import issuableEventHub from '~/issues/list/eventhub'; +import getIssuesQuery from 'ee_else_ce/issues/list/queries/get_issues.query.graphql'; +import getIssuesCountQuery from 'ee_else_ce/issues/list/queries/get_issues_counts.query.graphql'; +import moveIssueMutation from '../../queries/move_issue.mutation.graphql'; + +export default { + name: 'MoveIssuesButton', + components: { + IssuableMoveDropdown, + GlAlert, + }, + props: { + projectFullPath: { + type: String, + required: true, + }, + projectsFetchPath: { + type: String, + required: true, + }, + }, + data() { + return { + selectedIssuables: [], + moveInProgress: false, + }; + }, + computed: { + cannotMoveTasksWarningTitle() { + if (this.tasksSelected && this.testCasesSelected) { + return s__('Issues|Tasks and test cases can not be moved.'); + } + + if (this.testCasesSelected) { + return s__('Issues|Test cases can not be moved.'); + } + + return s__('Issues|Tasks can not be moved.'); + }, + issuesSelected() { + return this.selectedIssuables.some((item) => item.type === WORK_ITEM_TYPE_ENUM_ISSUE); + }, + incidentsSelected() { + return this.selectedIssuables.some((item) => item.type === WORK_ITEM_TYPE_ENUM_INCIDENT); + }, + tasksSelected() { + return this.selectedIssuables.some((item) => item.type === WORK_ITEM_TYPE_ENUM_TASK); + }, + testCasesSelected() { + return this.selectedIssuables.some((item) => item.type === WORK_ITEM_TYPE_ENUM_TEST_CASE); + }, + }, + mounted() { + issuableEventHub.$on('issuables:issuableChecked', this.handleIssuableChecked); + }, + beforeDestroy() { + issuableEventHub.$off('issuables:issuableChecked', this.handleIssuableChecked); + }, + methods: { + handleIssuableChecked(issuable, value) { + if (value) { + this.selectedIssuables.push(issuable); + } else { + const index = this.selectedIssuables.indexOf(issuable); + if (index > -1) { + this.selectedIssuables.splice(index, 1); + } + } + }, + moveIssues(targetProject) { + const iids = this.selectedIssuables.reduce((result, issueData) => { + if ( + issueData.type === WORK_ITEM_TYPE_ENUM_ISSUE || + issueData.type === WORK_ITEM_TYPE_ENUM_INCIDENT + ) { + result.push(issueData.iid); + } + return result; + }, []); + + if (iids.length === 0) { + return; + } + + this.moveInProgress = true; + issuableEventHub.$emit('issuables:bulkMoveStarted'); + + const promises = iids.map((id) => { + return this.moveIssue(id, targetProject); + }); + + Promise.all(promises) + .then((promisesResult) => { + let foundError = false; + + for (const promiseResult of promisesResult) { + if (promiseResult.data.issueMove?.errors?.length) { + foundError = true; + logError( + `Error moving issue. Error message: ${promiseResult.data.issueMove.errors[0].message}`, + ); + } + } + + if (!foundError) { + const client = this.$apollo.provider.defaultClient; + client.refetchQueries({ + include: [getIssuesQuery, getIssuesCountQuery], + }); + this.moveInProgress = false; + this.selectedIssuables = []; + issuableEventHub.$emit('issuables:bulkMoveEnded'); + } else { + throw new Error(); + } + }) + .catch(() => { + this.moveInProgress = false; + issuableEventHub.$emit('issuables:bulkMoveEnded'); + + createFlash({ + message: s__(`Issues|There was an error while moving the issues.`), + }); + }); + }, + moveIssue(issueIid, targetProject) { + return this.$apollo.mutate({ + mutation: moveIssueMutation, + variables: { + moveIssueInput: { + projectPath: this.projectFullPath, + iid: issueIid, + targetProjectPath: targetProject.full_path, + }, + }, + }); + }, + }, + i18n: { + dropdownButtonTitle: s__('Issues|Move selected'), + }, +}; +</script> +<template> + <div> + <issuable-move-dropdown + :project-full-path="projectFullPath" + :projects-fetch-path="projectsFetchPath" + :move-in-progress="moveInProgress" + :disabled="!issuesSelected && !incidentsSelected" + :dropdown-header-title="$options.i18n.dropdownButtonTitle" + :dropdown-button-title="$options.i18n.dropdownButtonTitle" + @move-issuable="moveIssues" + /> + <gl-alert v-if="tasksSelected || testCasesSelected" :dismissible="false" variant="warning"> + {{ cannotMoveTasksWarningTitle }} + </gl-alert> + </div> +</template> diff --git a/app/assets/javascripts/sidebar/components/status/status_dropdown.vue b/app/assets/javascripts/sidebar/components/status/status_dropdown.vue new file mode 100644 index 00000000000..7763ec00091 --- /dev/null +++ b/app/assets/javascripts/sidebar/components/status/status_dropdown.vue @@ -0,0 +1,57 @@ +<script> +import { GlDropdown, GlDropdownItem } from '@gitlab/ui'; +import { __ } from '~/locale'; +import { statusDropdownOptions } from '../../constants'; + +export default { + components: { + GlDropdown, + GlDropdownItem, + }, + data() { + return { + status: null, + }; + }, + computed: { + dropdownText() { + return this.status?.text ?? this.$options.i18n.defaultDropdownText; + }, + selectedValue() { + return this.status?.value; + }, + }, + methods: { + onDropdownItemClick(statusOption) { + // clear status if the currently checked status is clicked again + if (this.status?.value === statusOption.value) { + this.status = null; + } else { + this.status = statusOption; + } + }, + }, + i18n: { + dropdownTitle: __('Change status'), + defaultDropdownText: __('Select status'), + }, + statusDropdownOptions, +}; +</script> +<template> + <div> + <input type="hidden" name="update[state_event]" :value="selectedValue" /> + <gl-dropdown :text="dropdownText" :title="$options.i18n.dropdownTitle" class="gl-w-full"> + <gl-dropdown-item + v-for="statusOption in $options.statusDropdownOptions" + :key="statusOption.value" + :is-checked="selectedValue === statusOption.value" + is-check-item + :title="statusOption.text" + @click="onDropdownItemClick(statusOption)" + > + {{ statusOption.text }} + </gl-dropdown-item> + </gl-dropdown> + </div> +</template> diff --git a/app/assets/javascripts/sidebar/components/subscriptions/subscriptions_dropdown.vue b/app/assets/javascripts/sidebar/components/subscriptions/subscriptions_dropdown.vue new file mode 100644 index 00000000000..4c3ba76d12d --- /dev/null +++ b/app/assets/javascripts/sidebar/components/subscriptions/subscriptions_dropdown.vue @@ -0,0 +1,51 @@ +<script> +import { GlDropdown, GlDropdownItem } from '@gitlab/ui'; +import { __ } from '~/locale'; +import { subscriptionsDropdownOptions } from '../../constants'; + +export default { + subscriptionsDropdownOptions, + i18n: { + defaultDropdownText: __('Select subscription'), + headerText: __('Change subscription'), + }, + components: { + GlDropdown, + GlDropdownItem, + }, + data() { + return { + subscription: undefined, + }; + }, + computed: { + dropdownText() { + return this.subscription?.text ?? this.$options.i18n.defaultDropdownText; + }, + selectedValue() { + return this.subscription?.value; + }, + }, + methods: { + handleClick(option) { + this.subscription = option.value === this.subscription?.value ? undefined : option; + }, + }, +}; +</script> +<template> + <div> + <input type="hidden" name="update[subscription_event]" :value="selectedValue" /> + <gl-dropdown class="gl-w-full" :header-text="$options.i18n.headerText" :text="dropdownText"> + <gl-dropdown-item + v-for="subscriptionsOption in $options.subscriptionsDropdownOptions" + :key="subscriptionsOption.value" + is-check-item + :is-checked="selectedValue === subscriptionsOption.value" + @click="handleClick(subscriptionsOption)" + > + {{ subscriptionsOption.text }} + </gl-dropdown-item> + </gl-dropdown> + </div> +</template> diff --git a/app/assets/javascripts/sidebar/constants.js b/app/assets/javascripts/sidebar/constants.js index 67b9b540e91..499b03cd931 100644 --- a/app/assets/javascripts/sidebar/constants.js +++ b/app/assets/javascripts/sidebar/constants.js @@ -350,3 +350,25 @@ export const escalationStatusQuery = getEscalationStatusQuery; export const escalationStatusMutation = updateEscalationStatusMutation; export const HOW_TO_TRACK_TIME = __('How to track time'); + +export const statusDropdownOptions = [ + { + text: __('Open'), + value: 'reopen', + }, + { + text: __('Closed'), + value: 'close', + }, +]; + +export const subscriptionsDropdownOptions = [ + { + text: __('Subscribe'), + value: 'subscribe', + }, + { + text: __('Unsubscribe'), + value: 'unsubscribe', + }, +]; diff --git a/app/assets/javascripts/sidebar/mount_sidebar.js b/app/assets/javascripts/sidebar/mount_sidebar.js index b37486283ca..3fc98f86316 100644 --- a/app/assets/javascripts/sidebar/mount_sidebar.js +++ b/app/assets/javascripts/sidebar/mount_sidebar.js @@ -6,6 +6,7 @@ import { convertToGraphQLId } from '~/graphql_shared/utils'; import initInviteMembersModal from '~/invite_members/init_invite_members_modal'; import initInviteMembersTrigger from '~/invite_members/init_invite_members_trigger'; import { IssuableType } from '~/issues/constants'; +import { gqlClient } from '~/issues/list/graphql'; import { isInIssuePage, isInDesignPage, @@ -14,33 +15,36 @@ import { parseBoolean, } from '~/lib/utils/common_utils'; import { __ } from '~/locale'; -import CollapsedAssigneeList from '~/sidebar/components/assignees/collapsed_assignee_list.vue'; -import SidebarAssigneesWidget from '~/sidebar/components/assignees/sidebar_assignees_widget.vue'; -import SidebarConfidentialityWidget from '~/sidebar/components/confidential/sidebar_confidentiality_widget.vue'; -import SidebarDueDateWidget from '~/sidebar/components/date/sidebar_date_widget.vue'; -import MilestoneDropdown from '~/sidebar/components/milestone/milestone_dropdown.vue'; -import SidebarParticipantsWidget from '~/sidebar/components/participants/sidebar_participants_widget.vue'; -import SidebarReferenceWidget from '~/sidebar/components/reference/sidebar_reference_widget.vue'; -import SidebarDropdownWidget from '~/sidebar/components/sidebar_dropdown_widget.vue'; -import SidebarTodoWidget from '~/sidebar/components/todo_toggle/sidebar_todo_widget.vue'; import { apolloProvider } from '~/graphql_shared/issuable_client'; -import trackShowInviteMemberLink from '~/sidebar/track_invite_members'; import { DropdownVariant } from '~/vue_shared/components/sidebar/labels_select_vue/constants'; import LabelsSelectWidget from '~/vue_shared/components/sidebar/labels_select_widget/labels_select_root.vue'; import { LabelType } from '~/vue_shared/components/sidebar/labels_select_widget/constants'; -import Translate from '../vue_shared/translate'; +import Translate from '~/vue_shared/translate'; +import CollapsedAssigneeList from './components/assignees/collapsed_assignee_list.vue'; import SidebarAssignees from './components/assignees/sidebar_assignees.vue'; -import CopyEmailToClipboard from './components/copy_email_to_clipboard.vue'; +import SidebarAssigneesWidget from './components/assignees/sidebar_assignees_widget.vue'; +import SidebarConfidentialityWidget from './components/confidential/sidebar_confidentiality_widget.vue'; +import CopyEmailToClipboard from './components/copy_email/copy_email_to_clipboard.vue'; +import SidebarDueDateWidget from './components/date/sidebar_date_widget.vue'; import SidebarEscalationStatus from './components/incidents/sidebar_escalation_status.vue'; import IssuableLockForm from './components/lock/issuable_lock_form.vue'; +import MilestoneDropdown from './components/milestone/milestone_dropdown.vue'; +import MoveIssuesButton from './components/move/move_issues_button.vue'; +import SidebarParticipantsWidget from './components/participants/sidebar_participants_widget.vue'; +import SidebarReferenceWidget from './components/reference/sidebar_reference_widget.vue'; import SidebarReviewers from './components/reviewers/sidebar_reviewers.vue'; import SidebarReviewersInputs from './components/reviewers/sidebar_reviewers_inputs.vue'; import SidebarSeverity from './components/severity/sidebar_severity.vue'; +import SidebarDropdownWidget from './components/sidebar_dropdown_widget.vue'; +import StatusDropdown from './components/status/status_dropdown.vue'; import SidebarSubscriptionsWidget from './components/subscriptions/sidebar_subscriptions_widget.vue'; +import SubscriptionsDropdown from './components/subscriptions/subscriptions_dropdown.vue'; import SidebarTimeTracking from './components/time_tracking/sidebar_time_tracking.vue'; +import SidebarTodoWidget from './components/todo_toggle/sidebar_todo_widget.vue'; import { IssuableAttributeType } from './constants'; -import SidebarMoveIssue from './lib/sidebar_move_issue'; import CrmContacts from './components/crm_contacts/crm_contacts.vue'; +import SidebarMoveIssue from './lib/sidebar_move_issue'; +import trackShowInviteMemberLink from './track_invite_members'; Vue.use(Translate); Vue.use(VueApollo); @@ -635,6 +639,59 @@ function mountCopyEmailToClipboard() { }); } +export function mountMoveIssuesButton() { + const el = document.querySelector('.js-move-issues'); + + if (!el) { + return null; + } + + Vue.use(VueApollo); + + return new Vue({ + el, + name: 'MoveIssuesRoot', + apolloProvider: new VueApollo({ + defaultClient: gqlClient, + }), + render: (createElement) => + createElement(MoveIssuesButton, { + props: { + projectFullPath: el.dataset.projectFullPath, + projectsFetchPath: el.dataset.projectsFetchPath, + }, + }), + }); +} + +export function mountStatusDropdown() { + const el = document.querySelector('.js-status-dropdown'); + + if (!el) { + return null; + } + + return new Vue({ + el, + name: 'StatusDropdownRoot', + render: (createElement) => createElement(StatusDropdown), + }); +} + +export function mountSubscriptionsDropdown() { + const el = document.querySelector('.js-subscriptions-dropdown'); + + if (!el) { + return null; + } + + return new Vue({ + el, + name: 'SubscriptionsDropdownRoot', + render: (createElement) => createElement(SubscriptionsDropdown), + }); +} + const isAssigneesWidgetShown = (isInIssuePage() || isInDesignPage() || isInMRPage()) && gon.features.issueAssigneesWidget; diff --git a/app/assets/javascripts/sidebar/queries/move_issue.mutation.graphql b/app/assets/javascripts/sidebar/queries/move_issue.mutation.graphql new file mode 100644 index 00000000000..d350072425b --- /dev/null +++ b/app/assets/javascripts/sidebar/queries/move_issue.mutation.graphql @@ -0,0 +1,5 @@ +mutation moveIssue($moveIssueInput: IssueMoveInput!) { + issueMove(input: $moveIssueInput) { + errors + } +} |