diff options
Diffstat (limited to 'app/assets/javascripts/issuable/bulk_update_sidebar')
8 files changed, 0 insertions, 667 deletions
diff --git a/app/assets/javascripts/issuable/bulk_update_sidebar/components/graphql/mutations/move_issue.mutation.graphql b/app/assets/javascripts/issuable/bulk_update_sidebar/components/graphql/mutations/move_issue.mutation.graphql deleted file mode 100644 index d350072425b..00000000000 --- a/app/assets/javascripts/issuable/bulk_update_sidebar/components/graphql/mutations/move_issue.mutation.graphql +++ /dev/null @@ -1,5 +0,0 @@ -mutation moveIssue($moveIssueInput: IssueMoveInput!) { - issueMove(input: $moveIssueInput) { - errors - } -} diff --git a/app/assets/javascripts/issuable/bulk_update_sidebar/components/move_issues_button.vue b/app/assets/javascripts/issuable/bulk_update_sidebar/components/move_issues_button.vue deleted file mode 100644 index 6e287ac3bb7..00000000000 --- a/app/assets/javascripts/issuable/bulk_update_sidebar/components/move_issues_button.vue +++ /dev/null @@ -1,171 +0,0 @@ -<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 './graphql/mutations/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/issuable/bulk_update_sidebar/components/status_dropdown.vue b/app/assets/javascripts/issuable/bulk_update_sidebar/components/status_dropdown.vue deleted file mode 100644 index ba94932289e..00000000000 --- a/app/assets/javascripts/issuable/bulk_update_sidebar/components/status_dropdown.vue +++ /dev/null @@ -1,57 +0,0 @@ -<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/issuable/bulk_update_sidebar/components/subscriptions_dropdown.vue b/app/assets/javascripts/issuable/bulk_update_sidebar/components/subscriptions_dropdown.vue deleted file mode 100644 index 8774b065c22..00000000000 --- a/app/assets/javascripts/issuable/bulk_update_sidebar/components/subscriptions_dropdown.vue +++ /dev/null @@ -1,51 +0,0 @@ -<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/issuable/bulk_update_sidebar/constants.js b/app/assets/javascripts/issuable/bulk_update_sidebar/constants.js deleted file mode 100644 index 68133ceb3c7..00000000000 --- a/app/assets/javascripts/issuable/bulk_update_sidebar/constants.js +++ /dev/null @@ -1,23 +0,0 @@ -import { __ } from '~/locale'; - -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/issuable/bulk_update_sidebar/index.js b/app/assets/javascripts/issuable/bulk_update_sidebar/index.js deleted file mode 100644 index b7cb805ee37..00000000000 --- a/app/assets/javascripts/issuable/bulk_update_sidebar/index.js +++ /dev/null @@ -1,75 +0,0 @@ -import Vue from 'vue'; -import VueApollo from 'vue-apollo'; -import { gqlClient } from '../../issues/list/graphql'; -import StatusDropdown from './components/status_dropdown.vue'; -import SubscriptionsDropdown from './components/subscriptions_dropdown.vue'; -import MoveIssuesButton from './components/move_issues_button.vue'; -import issuableBulkUpdateActions from './issuable_bulk_update_actions'; -import IssuableBulkUpdateSidebar from './issuable_bulk_update_sidebar'; - -export function initBulkUpdateSidebar(prefixId) { - const el = document.querySelector('.issues-bulk-update'); - - if (!el) { - return; - } - - issuableBulkUpdateActions.init({ prefixId }); - new IssuableBulkUpdateSidebar(); // eslint-disable-line no-new -} - -export function initStatusDropdown() { - const el = document.querySelector('.js-status-dropdown'); - - if (!el) { - return null; - } - - return new Vue({ - el, - name: 'StatusDropdownRoot', - render: (createElement) => createElement(StatusDropdown), - }); -} - -export function initSubscriptionsDropdown() { - const el = document.querySelector('.js-subscriptions-dropdown'); - - if (!el) { - return null; - } - - return new Vue({ - el, - name: 'SubscriptionsDropdownRoot', - render: (createElement) => createElement(SubscriptionsDropdown), - }); -} - -export function initMoveIssuesButton() { - const el = document.querySelector('.js-move-issues'); - - if (!el) { - return null; - } - - const { dataset } = el; - - Vue.use(VueApollo); - const apolloProvider = new VueApollo({ - defaultClient: gqlClient, - }); - - return new Vue({ - el, - name: 'MoveIssuesRoot', - apolloProvider, - render: (createElement) => - createElement(MoveIssuesButton, { - props: { - projectFullPath: dataset.projectFullPath, - projectsFetchPath: dataset.projectsFetchPath, - }, - }), - }); -} diff --git a/app/assets/javascripts/issuable/bulk_update_sidebar/issuable_bulk_update_actions.js b/app/assets/javascripts/issuable/bulk_update_sidebar/issuable_bulk_update_actions.js deleted file mode 100644 index 14824820c0d..00000000000 --- a/app/assets/javascripts/issuable/bulk_update_sidebar/issuable_bulk_update_actions.js +++ /dev/null @@ -1,126 +0,0 @@ -import $ from 'jquery'; -import { difference, intersection, union } from 'lodash'; -import createFlash from '~/flash'; -import axios from '~/lib/utils/axios_utils'; -import { __ } from '~/locale'; - -export default { - init({ form, issues, prefixId } = {}) { - this.prefixId = prefixId || 'issue_'; - this.form = form || this.getElement('.bulk-update'); - this.$labelDropdown = this.form.find('.js-label-select'); - this.issues = issues || this.getElement('.issues-list .issue'); - this.willUpdateLabels = false; - this.bindEvents(); - }, - - bindEvents() { - // eslint-disable-next-line @gitlab/no-global-event-off - return this.form.off('submit').on('submit', this.onFormSubmit.bind(this)); - }, - - onFormSubmit(e) { - e.preventDefault(); - return this.submit(); - }, - - submit() { - axios[this.form.attr('method')](this.form.attr('action'), this.getFormDataAsObject()) - .then(() => window.location.reload()) - .catch(() => this.onFormSubmitFailure()); - }, - - onFormSubmitFailure() { - this.form.find('[type="submit"]').enable(); - return createFlash({ - message: __('Issue update failed'), - }); - }, - - /** - * Simple form serialization, it will return just what we need - * Returns key/value pairs from form data - */ - - getFormDataAsObject() { - const formData = { - update: { - state_event: this.form.find('input[name="update[state_event]"]').val(), - assignee_ids: [this.form.find('input[name="update[assignee_ids][]"]').val()], - milestone_id: this.form.find('input[name="update[milestone_id]"]').val(), - issuable_ids: this.form.find('input[name="update[issuable_ids]"]').val(), - subscription_event: this.form.find('input[name="update[subscription_event]"]').val(), - health_status: this.form.find('input[name="update[health_status]"]').val(), - epic_id: this.form.find('input[name="update[epic_id]"]').val(), - sprint_id: this.form.find('input[name="update[iteration_id]"]').val(), - add_label_ids: [], - remove_label_ids: [], - }, - }; - if (this.willUpdateLabels) { - formData.update.add_label_ids = this.$labelDropdown.data('user-checked'); - formData.update.remove_label_ids = this.$labelDropdown.data('user-unchecked'); - } - return formData; - }, - - setOriginalDropdownData() { - const $labelSelect = $('.bulk-update .js-label-select'); - const userCheckedIds = $labelSelect.data('user-checked') || []; - const userUncheckedIds = $labelSelect.data('user-unchecked') || []; - - // Common labels plus user checked labels minus user unchecked labels - const checkedIdsToShow = difference( - union(this.getOriginalCommonIds(), userCheckedIds), - userUncheckedIds, - ); - - // Indeterminate labels minus user checked labels minus user unchecked labels - const indeterminateIdsToShow = difference( - this.getOriginalIndeterminateIds(), - userCheckedIds, - userUncheckedIds, - ); - - $labelSelect.data('marked', checkedIdsToShow); - $labelSelect.data('indeterminate', indeterminateIdsToShow); - }, - - // From issuable's initial bulk selection - getOriginalCommonIds() { - const labelIds = []; - this.getElement('.issuable-list input[type="checkbox"]:checked').each((i, el) => { - labelIds.push(this.getElement(`#${this.prefixId}${el.dataset.id}`).data('labels')); - }); - return intersection.apply(this, labelIds); - }, - - // From issuable's initial bulk selection - getOriginalIndeterminateIds() { - const uniqueIds = []; - const labelIds = []; - let issuableLabels = []; - - // Collect unique label IDs for all checked issues - this.getElement('.issuable-list input[type="checkbox"]:checked').each((i, el) => { - issuableLabels = this.getElement(`#${this.prefixId}${el.dataset.id}`).data('labels'); - issuableLabels.forEach((labelId) => { - // Store unique IDs - if (uniqueIds.indexOf(labelId) === -1) { - uniqueIds.push(labelId); - } - }); - // Store array of IDs per issuable - labelIds.push(issuableLabels); - }); - // Add uniqueIds to add it as argument for _.intersection - labelIds.unshift(uniqueIds); - // Return IDs that are present but not in all selected issuables - return uniqueIds.filter((x) => !intersection.apply(this, labelIds).includes(x)); - }, - - getElement(selector) { - this.scopeEl = this.scopeEl || $('.content'); - return this.scopeEl.find(selector); - }, -}; diff --git a/app/assets/javascripts/issuable/bulk_update_sidebar/issuable_bulk_update_sidebar.js b/app/assets/javascripts/issuable/bulk_update_sidebar/issuable_bulk_update_sidebar.js deleted file mode 100644 index b46a95c7dfa..00000000000 --- a/app/assets/javascripts/issuable/bulk_update_sidebar/issuable_bulk_update_sidebar.js +++ /dev/null @@ -1,159 +0,0 @@ -/* eslint-disable class-methods-use-this, no-new */ - -import $ from 'jquery'; -import issuableEventHub from '~/issues/list/eventhub'; -import LabelsSelect from '~/labels/labels_select'; -import { mountMilestoneDropdown } from '~/sidebar/mount_sidebar'; -import IssuableBulkUpdateActions from './issuable_bulk_update_actions'; - -const HIDDEN_CLASS = 'hidden'; -const DISABLED_CONTENT_CLASS = 'disabled-content'; -const SIDEBAR_EXPANDED_CLASS = 'right-sidebar-expanded issuable-bulk-update-sidebar'; -const SIDEBAR_COLLAPSED_CLASS = 'right-sidebar-collapsed issuable-bulk-update-sidebar'; - -export default class IssuableBulkUpdateSidebar { - constructor() { - this.initDomElements(); - this.bindEvents(); - this.initDropdowns(); - this.setupBulkUpdateActions(); - } - - initDomElements() { - this.$page = $('.layout-page'); - this.$sidebar = $('.right-sidebar'); - this.$sidebarInnerContainer = this.$sidebar.find('.issuable-sidebar'); - this.$bulkEditCancelBtn = $('.js-bulk-update-menu-hide'); - this.$bulkEditSubmitBtn = $('.js-update-selected-issues'); - this.$bulkUpdateEnableBtn = $('.js-bulk-update-toggle'); - this.$otherFilters = $('.issues-other-filters'); - this.$checkAllContainer = $('.check-all-holder'); - this.$issueChecks = $('.issue-check'); - this.$issuesList = $('.issuable-list input[type="checkbox"]'); - this.$issuableIdsInput = $('#update_issuable_ids'); - } - - bindEvents() { - this.$bulkUpdateEnableBtn.on('click', (e) => this.toggleBulkEdit(e, true)); - this.$bulkEditCancelBtn.on('click', (e) => this.toggleBulkEdit(e, false)); - this.$checkAllContainer.on('click', (e) => this.selectAll(e)); - this.$issuesList.on('change', () => this.updateFormState()); - this.$bulkEditSubmitBtn.on('click', () => this.prepForSubmit()); - this.$checkAllContainer.on('click', () => this.updateFormState()); - - // The event hub connects this bulk update logic with `issues_list_app.vue`. - // We can remove it once we've refactored the issues list page bulk edit sidebar to Vue. - // https://gitlab.com/gitlab-org/gitlab/-/issues/325874 - issuableEventHub.$on('issuables:enableBulkEdit', () => this.toggleBulkEdit(null, true)); - issuableEventHub.$on('issuables:updateBulkEdit', () => this.updateFormState()); - - // These events are connected to the logic inside `move_issues_button.vue`, - // so that only one action can be performed at a time - issuableEventHub.$on('issuables:bulkMoveStarted', () => this.toggleSubmitButtonDisabled(true)); - issuableEventHub.$on('issuables:bulkMoveEnded', () => this.updateFormState()); - } - - initDropdowns() { - new LabelsSelect(); - mountMilestoneDropdown(); - - // Checking IS_EE and using ee_else_ce is odd, but we do it here to satisfy - // the import/no-unresolved lint rule when FOSS_ONLY=1, even though at - // runtime this block won't execute. - if (IS_EE) { - import('ee_else_ce/sidebar/mount_sidebar') - .then(({ mountEpicDropdown, mountHealthStatusDropdown, mountIterationDropdown }) => { - mountEpicDropdown(); - mountHealthStatusDropdown(); - mountIterationDropdown(); - }) - .catch(() => {}); - } - } - - setupBulkUpdateActions() { - IssuableBulkUpdateActions.setOriginalDropdownData(); - } - - updateFormState() { - const noCheckedIssues = !$('.issuable-list input[type="checkbox"]:checked').length; - - this.toggleSubmitButtonDisabled(noCheckedIssues); - this.updateSelectedIssuableIds(); - - IssuableBulkUpdateActions.setOriginalDropdownData(); - - issuableEventHub.$emit('issuables:selectionChanged', !noCheckedIssues); - } - - prepForSubmit() { - // if submit button is disabled, submission is blocked. This ensures we disable after - // form submission is carried out - setTimeout(() => this.$bulkEditSubmitBtn.disable()); - this.updateSelectedIssuableIds(); - } - - toggleBulkEdit(e, enable) { - e?.preventDefault(); - - issuableEventHub.$emit('issuables:toggleBulkEdit', enable); - - this.toggleSidebarDisplay(enable); - this.toggleBulkEditButtonDisabled(enable); - this.toggleOtherFiltersDisabled(enable); - this.toggleCheckboxDisplay(enable); - } - - updateSelectedIssuableIds() { - this.$issuableIdsInput.val(IssuableBulkUpdateSidebar.getCheckedIssueIds()); - } - - selectAll() { - const checkAllButtonState = this.$checkAllContainer.find('input').prop('checked'); - - this.$issuesList.prop('checked', checkAllButtonState); - } - - toggleSidebarDisplay(show) { - this.$page.toggleClass(SIDEBAR_EXPANDED_CLASS, show); - this.$page.toggleClass(SIDEBAR_COLLAPSED_CLASS, !show); - this.$sidebarInnerContainer.toggleClass(HIDDEN_CLASS, !show); - this.$sidebar.toggleClass(SIDEBAR_EXPANDED_CLASS, show); - this.$sidebar.toggleClass(SIDEBAR_COLLAPSED_CLASS, !show); - } - - toggleBulkEditButtonDisabled(disable) { - if (disable) { - this.$bulkUpdateEnableBtn.disable(); - } else { - this.$bulkUpdateEnableBtn.enable(); - } - } - - toggleCheckboxDisplay(show) { - this.$checkAllContainer.toggleClass(HIDDEN_CLASS, !show); - this.$issueChecks.toggleClass(HIDDEN_CLASS, !show); - } - - toggleOtherFiltersDisabled(disable) { - this.$otherFilters.toggleClass(DISABLED_CONTENT_CLASS, disable); - } - - toggleSubmitButtonDisabled(disable) { - if (disable) { - this.$bulkEditSubmitBtn.disable(); - } else { - this.$bulkEditSubmitBtn.enable(); - } - } - - static getCheckedIssueIds() { - const $checkedIssues = $('.issuable-list input[type="checkbox"]:checked'); - - if ($checkedIssues.length > 0) { - return $.map($checkedIssues, (value) => $(value).data('id')); - } - - return []; - } -} |