From 859a6fb938bb9ee2a317c46dfa4fcc1af49608f0 Mon Sep 17 00:00:00 2001 From: GitLab Bot Date: Thu, 18 Feb 2021 10:34:06 +0000 Subject: Add latest changes from gitlab-org/gitlab@13-9-stable-ee --- app/assets/javascripts/boards/boards_util.js | 14 +- .../components/board_add_new_column_trigger.vue | 21 ++ .../boards/components/board_assignee_dropdown.vue | 196 ----------- .../javascripts/boards/components/board_card.vue | 7 +- .../boards/components/board_card_layout.vue | 26 +- .../components/board_card_layout_deprecated.vue | 102 ++++++ .../javascripts/boards/components/board_column.vue | 18 +- .../boards/components/board_column_deprecated.vue | 16 +- .../components/board_configuration_options.vue | 6 + .../boards/components/board_content.vue | 12 +- .../javascripts/boards/components/board_form.vue | 26 +- .../javascripts/boards/components/board_list.vue | 10 +- .../boards/components/board_list_deprecated.vue | 20 +- .../boards/components/board_list_header.vue | 43 ++- .../components/board_list_header_deprecated.vue | 41 ++- .../boards/components/board_new_issue.vue | 6 +- .../components/board_new_issue_deprecated.vue | 4 +- .../boards/components/board_settings_sidebar.vue | 19 +- .../javascripts/boards/components/board_sidebar.js | 67 +--- .../boards/components/boards_selector.vue | 38 ++- .../components/boards_selector_deprecated.vue | 357 +++++++++++++++++++++ .../boards/components/issue_card_inner.vue | 10 +- .../components/issue_card_inner_deprecated.vue | 6 +- .../boards/components/issue_due_date.vue | 4 +- .../boards/components/issue_time_estimate.vue | 2 +- .../boards/components/modal/empty_state.vue | 2 +- .../javascripts/boards/components/modal/filters.js | 2 +- .../javascripts/boards/components/modal/footer.vue | 4 +- .../javascripts/boards/components/modal/header.vue | 4 +- .../javascripts/boards/components/modal/index.vue | 8 +- .../javascripts/boards/components/modal/list.vue | 2 +- .../boards/components/modal/lists_dropdown.vue | 2 +- .../javascripts/boards/components/modal/tabs.vue | 2 +- .../boards/components/new_list_dropdown.js | 41 ++- .../boards/components/project_select.vue | 2 +- .../components/project_select_deprecated.vue | 4 +- .../components/sidebar/board_sidebar_due_date.vue | 20 +- .../sidebar/board_sidebar_issue_title.vue | 62 ++-- .../sidebar/board_sidebar_labels_select.vue | 8 +- .../sidebar/board_sidebar_milestone_select.vue | 86 +++-- .../sidebar/board_sidebar_subscription.vue | 2 +- .../javascripts/boards/components/toggle_focus.vue | 52 +++ app/assets/javascripts/boards/constants.js | 20 ++ .../javascripts/boards/filtered_search_boards.js | 8 +- .../javascripts/boards/filters/due_date_filters.js | 2 +- .../boards/graphql/group_milestones.query.graphql | 17 - .../graphql/project_milestones.query.graphql | 17 + app/assets/javascripts/boards/index.js | 50 ++- app/assets/javascripts/boards/models/issue.js | 6 +- app/assets/javascripts/boards/models/iteration.js | 9 + app/assets/javascripts/boards/models/list.js | 11 +- .../boards/mount_multiple_boards_switcher.js | 20 +- app/assets/javascripts/boards/stores/actions.js | 105 +++--- .../javascripts/boards/stores/boards_store.js | 22 +- app/assets/javascripts/boards/stores/getters.js | 10 +- app/assets/javascripts/boards/stores/index.js | 4 +- .../javascripts/boards/stores/mutation_types.js | 7 +- app/assets/javascripts/boards/stores/mutations.js | 34 +- app/assets/javascripts/boards/stores/state.js | 4 + app/assets/javascripts/boards/toggle_focus.js | 48 +-- 60 files changed, 1128 insertions(+), 640 deletions(-) create mode 100644 app/assets/javascripts/boards/components/board_add_new_column_trigger.vue delete mode 100644 app/assets/javascripts/boards/components/board_assignee_dropdown.vue create mode 100644 app/assets/javascripts/boards/components/board_card_layout_deprecated.vue create mode 100644 app/assets/javascripts/boards/components/boards_selector_deprecated.vue create mode 100644 app/assets/javascripts/boards/components/toggle_focus.vue delete mode 100644 app/assets/javascripts/boards/graphql/group_milestones.query.graphql create mode 100644 app/assets/javascripts/boards/graphql/project_milestones.query.graphql create mode 100644 app/assets/javascripts/boards/models/iteration.js (limited to 'app/assets/javascripts/boards') diff --git a/app/assets/javascripts/boards/boards_util.js b/app/assets/javascripts/boards/boards_util.js index 965d3571f42..13ad820477f 100644 --- a/app/assets/javascripts/boards/boards_util.js +++ b/app/assets/javascripts/boards/boards_util.js @@ -1,6 +1,6 @@ import { sortBy } from 'lodash'; -import { ListType } from './constants'; import { getIdFromGraphQLId } from '~/graphql_shared/utils'; +import { ListType, NOT_FILTER } from './constants'; export function getMilestone() { return null; @@ -144,6 +144,17 @@ export function isListDraggable(list) { return list.listType !== ListType.backlog && list.listType !== ListType.closed; } +export function transformNotFilters(filters) { + return Object.keys(filters) + .filter((key) => key.startsWith(NOT_FILTER)) + .reduce((obj, key) => { + return { + ...obj, + [key.substring(4, key.length - 1)]: filters[key], + }; + }, {}); +} + // EE-specific feature. Find the implementation in the `ee/`-folder export function transformBoardConfig() { return ''; @@ -157,4 +168,5 @@ export default { fullLabelId, fullIterationId, isListDraggable, + transformNotFilters, }; diff --git a/app/assets/javascripts/boards/components/board_add_new_column_trigger.vue b/app/assets/javascripts/boards/components/board_add_new_column_trigger.vue new file mode 100644 index 00000000000..85fca589279 --- /dev/null +++ b/app/assets/javascripts/boards/components/board_add_new_column_trigger.vue @@ -0,0 +1,21 @@ + + + diff --git a/app/assets/javascripts/boards/components/board_assignee_dropdown.vue b/app/assets/javascripts/boards/components/board_assignee_dropdown.vue deleted file mode 100644 index 5d381f9a570..00000000000 --- a/app/assets/javascripts/boards/components/board_assignee_dropdown.vue +++ /dev/null @@ -1,196 +0,0 @@ - - - diff --git a/app/assets/javascripts/boards/components/board_card.vue b/app/assets/javascripts/boards/components/board_card.vue index 31050eef83d..e6009343626 100644 --- a/app/assets/javascripts/boards/components/board_card.vue +++ b/app/assets/javascripts/boards/components/board_card.vue @@ -1,13 +1,14 @@ + + diff --git a/app/assets/javascripts/boards/components/board_column.vue b/app/assets/javascripts/boards/components/board_column.vue index 9f0eef844f6..41b9ee795eb 100644 --- a/app/assets/javascripts/boards/components/board_column.vue +++ b/app/assets/javascripts/boards/components/board_column.vue @@ -1,8 +1,8 @@ @@ -28,12 +32,14 @@ export default {

{{ __('Show the Open list') }} {{ __('Show the Closed list') }} diff --git a/app/assets/javascripts/boards/components/board_content.vue b/app/assets/javascripts/boards/components/board_content.vue index 19254343208..9b10e7d7db5 100644 --- a/app/assets/javascripts/boards/components/board_content.vue +++ b/app/assets/javascripts/boards/components/board_content.vue @@ -1,13 +1,13 @@ + + diff --git a/app/assets/javascripts/boards/components/issue_card_inner.vue b/app/assets/javascripts/boards/components/issue_card_inner.vue index 457d0d4dcd6..e5ea30df767 100644 --- a/app/assets/javascripts/boards/components/issue_card_inner.vue +++ b/app/assets/javascripts/boards/components/issue_card_inner.vue @@ -1,17 +1,17 @@ + + diff --git a/app/assets/javascripts/boards/constants.js b/app/assets/javascripts/boards/constants.js index 9264fac5eda..3ab89b2c9da 100644 --- a/app/assets/javascripts/boards/constants.js +++ b/app/assets/javascripts/boards/constants.js @@ -1,3 +1,5 @@ +import { __ } from '~/locale'; + export const BoardType = { project: 'project', group: 'group', @@ -6,16 +8,34 @@ export const BoardType = { export const ListType = { assignee: 'assignee', milestone: 'milestone', + iteration: 'iteration', backlog: 'backlog', closed: 'closed', label: 'label', }; +export const ListTypeTitles = { + assignee: __('Assignee'), + milestone: __('Milestone'), + iteration: __('Iteration'), + label: __('Label'), +}; + +export const formType = { + new: 'new', + delete: 'delete', + edit: 'edit', +}; + export const inactiveId = 0; export const ISSUABLE = 'issuable'; export const LIST = 'list'; +export const NOT_FILTER = 'not['; + +export const flashAnimationDuration = 2000; + export default { BoardType, ListType, diff --git a/app/assets/javascripts/boards/filtered_search_boards.js b/app/assets/javascripts/boards/filtered_search_boards.js index 94b35aadaf1..66580bdd30f 100644 --- a/app/assets/javascripts/boards/filtered_search_boards.js +++ b/app/assets/javascripts/boards/filtered_search_boards.js @@ -1,10 +1,10 @@ -import IssuableFilteredSearchTokenKeys from 'ee_else_ce/filtered_search/issuable_filtered_search_token_keys'; -import FilteredSearchManager from 'ee_else_ce/filtered_search/filtered_search_manager'; import { transformBoardConfig } from 'ee_else_ce/boards/boards_util'; +import FilteredSearchManager from 'ee_else_ce/filtered_search/filtered_search_manager'; +import IssuableFilteredSearchTokenKeys from 'ee_else_ce/filtered_search/issuable_filtered_search_token_keys'; +import { updateHistory } from '~/lib/utils/url_utility'; import FilteredSearchContainer from '../filtered_search/container'; -import boardsStore from './stores/boards_store'; import vuexstore from './stores'; -import { updateHistory } from '~/lib/utils/url_utility'; +import boardsStore from './stores/boards_store'; export default class FilteredSearchBoards extends FilteredSearchManager { constructor(store, updateUrl = false, cantEdit = []) { diff --git a/app/assets/javascripts/boards/filters/due_date_filters.js b/app/assets/javascripts/boards/filters/due_date_filters.js index c35dedde71b..1745ab3bab4 100644 --- a/app/assets/javascripts/boards/filters/due_date_filters.js +++ b/app/assets/javascripts/boards/filters/due_date_filters.js @@ -1,5 +1,5 @@ -import Vue from 'vue'; import dateFormat from 'dateformat'; +import Vue from 'vue'; Vue.filter('due-date', (value) => { const date = new Date(value); diff --git a/app/assets/javascripts/boards/graphql/group_milestones.query.graphql b/app/assets/javascripts/boards/graphql/group_milestones.query.graphql deleted file mode 100644 index f2ab12ef4a7..00000000000 --- a/app/assets/javascripts/boards/graphql/group_milestones.query.graphql +++ /dev/null @@ -1,17 +0,0 @@ -query groupMilestones( - $fullPath: ID! - $state: MilestoneStateEnum - $includeDescendants: Boolean - $searchTitle: String -) { - group(fullPath: $fullPath) { - milestones(state: $state, includeDescendants: $includeDescendants, searchTitle: $searchTitle) { - edges { - node { - id - title - } - } - } - } -} diff --git a/app/assets/javascripts/boards/graphql/project_milestones.query.graphql b/app/assets/javascripts/boards/graphql/project_milestones.query.graphql new file mode 100644 index 00000000000..776530ebb83 --- /dev/null +++ b/app/assets/javascripts/boards/graphql/project_milestones.query.graphql @@ -0,0 +1,17 @@ +query groupMilestones( + $fullPath: ID! + $state: MilestoneStateEnum + $includeAncestors: Boolean + $searchTitle: String +) { + project(fullPath: $fullPath) { + milestones(state: $state, includeAncestors: $includeAncestors, searchTitle: $searchTitle) { + edges { + node { + id + title + } + } + } + } +} diff --git a/app/assets/javascripts/boards/index.js b/app/assets/javascripts/boards/index.js index ef70a094f7c..859295318ed 100644 --- a/app/assets/javascripts/boards/index.js +++ b/app/assets/javascripts/boards/index.js @@ -1,4 +1,5 @@ import Vue from 'vue'; +import VueApollo from 'vue-apollo'; import { mapActions, mapGetters } from 'vuex'; import 'ee_else_ce/boards/models/issue'; @@ -6,41 +7,39 @@ import 'ee_else_ce/boards/models/list'; import BoardSidebar from 'ee_else_ce/boards/components/board_sidebar'; import initNewListDropdown from 'ee_else_ce/boards/components/new_list_dropdown'; import boardConfigToggle from 'ee_else_ce/boards/config_toggle'; -import toggleLabels from 'ee_else_ce/boards/toggle_labels'; -import toggleEpicsSwimlanes from 'ee_else_ce/boards/toggle_epics_swimlanes'; import { setWeightFetchingState, setEpicFetchingState, getMilestoneTitle, getBoardsModalData, } from 'ee_else_ce/boards/ee_functions'; - -import VueApollo from 'vue-apollo'; +import toggleEpicsSwimlanes from 'ee_else_ce/boards/toggle_epics_swimlanes'; +import toggleLabels from 'ee_else_ce/boards/toggle_labels'; +import BoardAddNewColumnTrigger from '~/boards/components/board_add_new_column_trigger.vue'; import BoardContent from '~/boards/components/board_content.vue'; import BoardExtraActions from '~/boards/components/board_extra_actions.vue'; -import createDefaultClient from '~/lib/graphql'; -import { deprecatedCreateFlash as Flash } from '~/flash'; -import { __ } from '~/locale'; import './models/label'; import './models/assignee'; - -import toggleFocusMode from '~/boards/toggle_focus'; -import FilteredSearchBoards from '~/boards/filtered_search_boards'; -import eventHub from '~/boards/eventhub'; -import sidebarEventHub from '~/sidebar/event_hub'; import '~/boards/models/milestone'; import '~/boards/models/project'; +import '~/boards/filters/due_date_filters'; +import BoardAddIssuesModal from '~/boards/components/modal/index.vue'; +import eventHub from '~/boards/eventhub'; +import FilteredSearchBoards from '~/boards/filtered_search_boards'; +import modalMixin from '~/boards/mixins/modal_mixins'; import store from '~/boards/stores'; import boardsStore from '~/boards/stores/boards_store'; import ModalStore from '~/boards/stores/modal_store'; -import modalMixin from '~/boards/mixins/modal_mixins'; -import '~/boards/filters/due_date_filters'; -import BoardAddIssuesModal from '~/boards/components/modal/index.vue'; +import toggleFocusMode from '~/boards/toggle_focus'; +import { deprecatedCreateFlash as Flash } from '~/flash'; +import createDefaultClient from '~/lib/graphql'; import { NavigationType, convertObjectPropsToCamelCase, parseBoolean, } from '~/lib/utils/common_utils'; +import { __ } from '~/locale'; +import sidebarEventHub from '~/sidebar/event_hub'; import mountMultipleBoardsSwitcher from './mount_multiple_boards_switcher'; Vue.use(VueApollo); @@ -73,6 +72,7 @@ export default () => { boardsStore.setTimeTrackingLimitToHours($boardApp.dataset.timeTrackingLimitToHours); } + // eslint-disable-next-line @gitlab/no-runtime-template-compiler issueBoardsApp = new Vue({ el: $boardApp, components: { @@ -86,7 +86,7 @@ export default () => { groupId: Number($boardApp.dataset.groupId), rootPath: $boardApp.dataset.rootPath, currentUserId: gon.current_user_id || null, - canUpdate: $boardApp.dataset.canUpdate, + canUpdate: parseBoolean($boardApp.dataset.canUpdate), labelsFetchPath: $boardApp.dataset.labelsFetchPath, labelsManagePath: $boardApp.dataset.labelsManagePath, labelsFilterBasePath: $boardApp.dataset.labelsFilterBasePath, @@ -275,7 +275,7 @@ export default () => { }, }); - // eslint-disable-next-line no-new + // eslint-disable-next-line no-new, @gitlab/no-runtime-template-compiler new Vue({ el: document.getElementById('js-add-list'), data: { @@ -287,6 +287,21 @@ export default () => { }, }); + const createColumnTriggerEl = document.querySelector('.js-create-column-trigger'); + if (createColumnTriggerEl) { + // eslint-disable-next-line no-new + new Vue({ + el: createColumnTriggerEl, + components: { + BoardAddNewColumnTrigger, + }, + store, + render(createElement) { + return createElement('board-add-new-column-trigger'); + }, + }); + } + boardConfigToggle(boardsStore); const issueBoardsModal = document.getElementById('js-add-issues-btn'); @@ -341,5 +356,6 @@ export default () => { mountMultipleBoardsSwitcher({ fullPath: $boardApp.dataset.fullPath, rootPath: $boardApp.dataset.boardsEndpoint, + recentBoardsEndpoint: $boardApp.dataset.recentBoardsEndpoint, }); }; diff --git a/app/assets/javascripts/boards/models/issue.js b/app/assets/javascripts/boards/models/issue.js index 1e77326ba9c..46d1239457d 100644 --- a/app/assets/javascripts/boards/models/issue.js +++ b/app/assets/javascripts/boards/models/issue.js @@ -6,8 +6,8 @@ import axios from '~/lib/utils/axios_utils'; import './label'; import { convertObjectPropsToCamelCase } from '~/lib/utils/common_utils'; -import IssueProject from './project'; import boardsStore from '../stores/boards_store'; +import IssueProject from './project'; class ListIssue { constructor(obj) { @@ -53,6 +53,10 @@ class ListIssue { return boardsStore.findIssueAssignee(this, findAssignee); } + setAssignees(assignees) { + boardsStore.setIssueAssignees(this, assignees); + } + removeAssignee(removeAssignee) { boardsStore.removeIssueAssignee(this, removeAssignee); } diff --git a/app/assets/javascripts/boards/models/iteration.js b/app/assets/javascripts/boards/models/iteration.js new file mode 100644 index 00000000000..b7bdc204f7c --- /dev/null +++ b/app/assets/javascripts/boards/models/iteration.js @@ -0,0 +1,9 @@ +export default class ListIteration { + constructor(obj) { + this.id = obj.id; + this.title = obj.title; + this.state = obj.state; + this.webUrl = obj.web_url || obj.webUrl; + this.description = obj.description; + } +} diff --git a/app/assets/javascripts/boards/models/list.js b/app/assets/javascripts/boards/models/list.js index be02ac7b889..6c6e2522d92 100644 --- a/app/assets/javascripts/boards/models/list.js +++ b/app/assets/javascripts/boards/models/list.js @@ -1,9 +1,10 @@ /* eslint-disable class-methods-use-this */ -import { __ } from '~/locale'; -import ListLabel from './label'; -import ListAssignee from './assignee'; import { deprecatedCreateFlash as flash } from '~/flash'; +import { __ } from '~/locale'; import boardsStore from '../stores/boards_store'; +import ListAssignee from './assignee'; +import ListIteration from './iteration'; +import ListLabel from './label'; import ListMilestone from './milestone'; import 'ee_else_ce/boards/models/issue'; @@ -43,6 +44,7 @@ class List { this.isExpandable = Boolean(typeInfo.isExpandable); this.isExpanded = !obj.collapsed; this.page = 1; + this.highlighted = obj.highlighted; this.loading = true; this.loadingMore = false; this.issues = obj.issues || []; @@ -57,6 +59,9 @@ class List { } else if (IS_EE && obj.milestone) { this.milestone = new ListMilestone(obj.milestone); this.title = this.milestone.title; + } else if (IS_EE && obj.iteration) { + this.iteration = new ListIteration(obj.iteration); + this.title = this.iteration.title; } // doNotFetchIssues is a temporary workaround until issues are fetched using GraphQL on issue boards diff --git a/app/assets/javascripts/boards/mount_multiple_boards_switcher.js b/app/assets/javascripts/boards/mount_multiple_boards_switcher.js index 738c8fb927e..fa58af24ba2 100644 --- a/app/assets/javascripts/boards/mount_multiple_boards_switcher.js +++ b/app/assets/javascripts/boards/mount_multiple_boards_switcher.js @@ -1,8 +1,12 @@ import Vue from 'vue'; import VueApollo from 'vue-apollo'; +import { mapGetters } from 'vuex'; +import BoardsSelector from '~/boards/components/boards_selector.vue'; +import BoardsSelectorDeprecated from '~/boards/components/boards_selector_deprecated.vue'; +import store from '~/boards/stores'; import createDefaultClient from '~/lib/graphql'; import { parseBoolean } from '~/lib/utils/common_utils'; -import BoardsSelector from '~/boards/components/boards_selector.vue'; +import glFeatureFlagMixin from '~/vue_shared/mixins/gl_feature_flags_mixin'; Vue.use(VueApollo); @@ -16,11 +20,15 @@ export default (params = {}) => { el: boardsSwitcherElement, components: { BoardsSelector, + BoardsSelectorDeprecated, }, + mixins: [glFeatureFlagMixin()], apolloProvider, + store, provide: { fullPath: params.fullPath, rootPath: params.rootPath, + recentBoardsEndpoint: params.recentBoardsEndpoint, }, data() { const { dataset } = boardsSwitcherElement; @@ -39,8 +47,16 @@ export default (params = {}) => { return { boardsSelectorProps }; }, + computed: { + ...mapGetters(['shouldUseGraphQL']), + }, render(createElement) { - return createElement(BoardsSelector, { + if (this.shouldUseGraphQL) { + return createElement(BoardsSelector, { + props: this.boardsSelectorProps, + }); + } + return createElement(BoardsSelectorDeprecated, { props: this.boardsSelectorProps, }); }, diff --git a/app/assets/javascripts/boards/stores/actions.js b/app/assets/javascripts/boards/stores/actions.js index 1d34f21798a..a7cf1e9e647 100644 --- a/app/assets/javascripts/boards/stores/actions.js +++ b/app/assets/javascripts/boards/stores/actions.js @@ -1,11 +1,9 @@ import { pick } from 'lodash'; - import boardListsQuery from 'ee_else_ce/boards/graphql/board_lists.query.graphql'; -import createGqClient, { fetchPolicies } from '~/lib/graphql'; +import { BoardType, ListType, inactiveId, flashAnimationDuration } from '~/boards/constants'; import { getIdFromGraphQLId } from '~/graphql_shared/utils'; +import createGqClient, { fetchPolicies } from '~/lib/graphql'; import { convertObjectPropsToCamelCase, urlParamsToObject } from '~/lib/utils/common_utils'; -import { BoardType, ListType, inactiveId } from '~/boards/constants'; -import * as types from './mutation_types'; import { formatBoardLists, formatListIssues, @@ -14,23 +12,22 @@ import { formatIssue, formatIssueInput, updateListPosition, + transformNotFilters, } from '../boards_util'; -import createFlash from '~/flash'; -import { __ } from '~/locale'; -import updateAssigneesMutation from '~/vue_shared/components/sidebar/queries/updateAssignees.mutation.graphql'; -import listsIssuesQuery from '../graphql/lists_issues.query.graphql'; import boardLabelsQuery from '../graphql/board_labels.query.graphql'; import createBoardListMutation from '../graphql/board_list_create.mutation.graphql'; -import updateBoardListMutation from '../graphql/board_list_update.mutation.graphql'; -import issueMoveListMutation from '../graphql/issue_move_list.mutation.graphql'; import destroyBoardListMutation from '../graphql/board_list_destroy.mutation.graphql'; +import updateBoardListMutation from '../graphql/board_list_update.mutation.graphql'; +import groupProjectsQuery from '../graphql/group_projects.query.graphql'; import issueCreateMutation from '../graphql/issue_create.mutation.graphql'; -import issueSetLabelsMutation from '../graphql/issue_set_labels.mutation.graphql'; +import issueMoveListMutation from '../graphql/issue_move_list.mutation.graphql'; import issueSetDueDateMutation from '../graphql/issue_set_due_date.mutation.graphql'; -import issueSetSubscriptionMutation from '../graphql/issue_set_subscription.mutation.graphql'; +import issueSetLabelsMutation from '../graphql/issue_set_labels.mutation.graphql'; import issueSetMilestoneMutation from '../graphql/issue_set_milestone.mutation.graphql'; +import issueSetSubscriptionMutation from '../graphql/issue_set_subscription.mutation.graphql'; import issueSetTitleMutation from '../graphql/issue_set_title.mutation.graphql'; -import groupProjectsQuery from '../graphql/group_projects.query.graphql'; +import listsIssuesQuery from '../graphql/lists_issues.query.graphql'; +import * as types from './mutation_types'; const notImplemented = () => { /* eslint-disable-next-line @gitlab/require-i18n-strings */ @@ -66,6 +63,7 @@ export default { 'releaseTag', 'search', ]); + filterParams.not = transformNotFilters(filters); commit(types.SET_FILTERS, filterParams); }, @@ -108,9 +106,31 @@ export default { .catch(() => commit(types.RECEIVE_BOARD_LISTS_FAILURE)); }, - createList: ({ state, commit, dispatch }, { backlog, labelId, milestoneId, assigneeId }) => { + highlightList: ({ commit, state }, listId) => { + if ([ListType.backlog, ListType.closed].includes(state.boardLists[listId].listType)) { + return; + } + + commit(types.ADD_LIST_TO_HIGHLIGHTED_LISTS, listId); + + setTimeout(() => { + commit(types.REMOVE_LIST_FROM_HIGHLIGHTED_LISTS, listId); + }, flashAnimationDuration); + }, + + createList: ( + { state, commit, dispatch, getters }, + { backlog, labelId, milestoneId, assigneeId }, + ) => { const { boardId } = state; + const existingList = getters.getListByLabelId(labelId); + + if (existingList) { + dispatch('highlightList', existingList.id); + return; + } + gqlClient .mutate({ mutation: createBoardListMutation, @@ -128,6 +148,7 @@ export default { } else { const list = data.boardListCreate?.list; dispatch('addList', list); + dispatch('highlightList', list.id); } }) .catch(() => commit(types.CREATE_LIST_FAILURE)); @@ -153,10 +174,10 @@ export default { variables, }) .then(({ data }) => { - const labels = data[boardType]?.labels; - return labels.nodes; - }) - .catch(() => commit(types.RECEIVE_LABELS_FAILURE)); + const labels = data[boardType]?.labels.nodes; + commit(types.RECEIVE_LABELS_SUCCESS, labels); + return labels; + }); }, moveList: ( @@ -308,34 +329,11 @@ export default { }, setAssignees: ({ commit, getters }, assigneeUsernames) => { - commit(types.SET_ASSIGNEE_LOADING, true); - - return gqlClient - .mutate({ - mutation: updateAssigneesMutation, - variables: { - iid: getters.activeIssue.iid, - projectPath: getters.activeIssue.referencePath.split('#')[0], - assigneeUsernames, - }, - }) - .then(({ data }) => { - const { nodes } = data.issueSetAssignees?.issue?.assignees || []; - - commit('UPDATE_ISSUE_BY_ID', { - issueId: getters.activeIssue.id, - prop: 'assignees', - value: nodes, - }); - - return nodes; - }) - .catch(() => { - createFlash({ message: __('An error occurred while updating assignees.') }); - }) - .finally(() => { - commit(types.SET_ASSIGNEE_LOADING, false); - }); + commit('UPDATE_ISSUE_BY_ID', { + issueId: getters.activeIssue.id, + prop: 'assignees', + value: assigneeUsernames, + }); }, setActiveIssueMilestone: async ({ commit, getters }, input) => { @@ -534,6 +532,21 @@ export default { commit(types.SET_SELECTED_PROJECT, project); }, + toggleBoardItemMultiSelection: ({ commit, state }, boardItem) => { + const { selectedBoardItems } = state; + const index = selectedBoardItems.indexOf(boardItem); + + if (index === -1) { + commit(types.ADD_BOARD_ITEM_TO_SELECTION, boardItem); + } else { + commit(types.REMOVE_BOARD_ITEM_FROM_SELECTION, boardItem); + } + }, + + setAddColumnFormVisibility: ({ commit }, visible) => { + commit(types.SET_ADD_COLUMN_FORM_VISIBLE, visible); + }, + fetchBacklog: () => { notImplemented(); }, diff --git a/app/assets/javascripts/boards/stores/boards_store.js b/app/assets/javascripts/boards/stores/boards_store.js index f59530ddf8f..fbff736c7e1 100644 --- a/app/assets/javascripts/boards/stores/boards_store.js +++ b/app/assets/javascripts/boards/stores/boards_store.js @@ -4,22 +4,22 @@ import { sortBy } from 'lodash'; import Vue from 'vue'; import BoardsStoreEE from 'ee_else_ce/boards/stores/boards_store_ee'; +import { getIdFromGraphQLId } from '~/graphql_shared/utils'; +import createDefaultClient from '~/lib/graphql'; +import axios from '~/lib/utils/axios_utils'; import { urlParamsToObject, getUrlParamsArray, parseBoolean, convertObjectPropsToCamelCase, } from '~/lib/utils/common_utils'; -import createDefaultClient from '~/lib/graphql'; -import axios from '~/lib/utils/axios_utils'; import { mergeUrlParams } from '~/lib/utils/url_utility'; -import { getIdFromGraphQLId } from '~/graphql_shared/utils'; +import { ListType, flashAnimationDuration } from '../constants'; import eventHub from '../eventhub'; -import { ListType } from '../constants'; -import IssueProject from '../models/project'; -import ListLabel from '../models/label'; import ListAssignee from '../models/assignee'; +import ListLabel from '../models/label'; import ListMilestone from '../models/milestone'; +import IssueProject from '../models/project'; const PER_PAGE = 20; export const gqlClient = createDefaultClient(); @@ -106,6 +106,11 @@ const boardsStore = { list .save() .then(() => { + list.highlighted = true; + setTimeout(() => { + list.highlighted = false; + }, flashAnimationDuration); + // Remove any new issues from the backlog // as they will be visible in the new list list.issues.forEach(backlogList.removeIssue.bind(backlogList)); @@ -117,7 +122,6 @@ const boardsStore = { }, updateNewListDropdown(listId) { - // eslint-disable-next-line no-unused-expressions document .querySelector(`.js-board-list-${getIdFromGraphQLId(listId)}`) ?.classList.remove('is-active'); @@ -720,6 +724,10 @@ const boardsStore = { } }, + setIssueAssignees(issue, assignees) { + issue.assignees = [...assignees]; + }, + removeIssueLabels(issue, labels) { labels.forEach(issue.removeLabel.bind(issue)); }, diff --git a/app/assets/javascripts/boards/stores/getters.js b/app/assets/javascripts/boards/stores/getters.js index d72b5c6fb8e..cab97088bc6 100644 --- a/app/assets/javascripts/boards/stores/getters.js +++ b/app/assets/javascripts/boards/stores/getters.js @@ -17,12 +17,20 @@ export default { return state.issues[state.activeId] || {}; }, + groupPathForActiveIssue: (_, getters) => { + const { referencePath = '' } = getters.activeIssue; + return referencePath.slice(0, referencePath.indexOf('/')); + }, + projectPathForActiveIssue: (_, getters) => { - const referencePath = getters.activeIssue.referencePath || ''; + const { referencePath = '' } = getters.activeIssue; return referencePath.slice(0, referencePath.indexOf('#')); }, getListByLabelId: (state) => (labelId) => { + if (!labelId) { + return null; + } return find(state.boardLists, (l) => l.label?.id === labelId); }, diff --git a/app/assets/javascripts/boards/stores/index.js b/app/assets/javascripts/boards/stores/index.js index 471b952a212..0a87c6ab821 100644 --- a/app/assets/javascripts/boards/stores/index.js +++ b/app/assets/javascripts/boards/stores/index.js @@ -1,9 +1,9 @@ import Vue from 'vue'; import Vuex from 'vuex'; -import state from 'ee_else_ce/boards/stores/state'; -import getters from 'ee_else_ce/boards/stores/getters'; import actions from 'ee_else_ce/boards/stores/actions'; +import getters from 'ee_else_ce/boards/stores/getters'; import mutations from 'ee_else_ce/boards/stores/mutations'; +import state from 'ee_else_ce/boards/stores/state'; Vue.use(Vuex); diff --git a/app/assets/javascripts/boards/stores/mutation_types.js b/app/assets/javascripts/boards/stores/mutation_types.js index 4697f39498a..a89e961ae2d 100644 --- a/app/assets/javascripts/boards/stores/mutation_types.js +++ b/app/assets/javascripts/boards/stores/mutation_types.js @@ -2,7 +2,7 @@ export const SET_INITIAL_BOARD_DATA = 'SET_INITIAL_BOARD_DATA'; export const SET_FILTERS = 'SET_FILTERS'; export const CREATE_LIST_SUCCESS = 'CREATE_LIST_SUCCESS'; export const CREATE_LIST_FAILURE = 'CREATE_LIST_FAILURE'; -export const RECEIVE_LABELS_FAILURE = 'RECEIVE_LABELS_FAILURE'; +export const RECEIVE_LABELS_SUCCESS = 'RECEIVE_LABELS_SUCCESS'; export const GENERATE_DEFAULT_LISTS_FAILURE = 'GENERATE_DEFAULT_LISTS_FAILURE'; export const RECEIVE_BOARD_LISTS_SUCCESS = 'RECEIVE_BOARD_LISTS_SUCCESS'; export const RECEIVE_BOARD_LISTS_FAILURE = 'RECEIVE_BOARD_LISTS_FAILURE'; @@ -40,3 +40,8 @@ export const REQUEST_GROUP_PROJECTS = 'REQUEST_GROUP_PROJECTS'; export const RECEIVE_GROUP_PROJECTS_SUCCESS = 'RECEIVE_GROUP_PROJECTS_SUCCESS'; export const RECEIVE_GROUP_PROJECTS_FAILURE = 'RECEIVE_GROUP_PROJECTS_FAILURE'; export const SET_SELECTED_PROJECT = 'SET_SELECTED_PROJECT'; +export const ADD_BOARD_ITEM_TO_SELECTION = 'ADD_BOARD_ITEM_TO_SELECTION'; +export const REMOVE_BOARD_ITEM_FROM_SELECTION = 'REMOVE_BOARD_ITEM_FROM_SELECTION'; +export const SET_ADD_COLUMN_FORM_VISIBLE = 'SET_ADD_COLUMN_FORM_VISIBLE'; +export const ADD_LIST_TO_HIGHLIGHTED_LISTS = 'ADD_LIST_TO_HIGHLIGHTED_LISTS'; +export const REMOVE_LIST_FROM_HIGHLIGHTED_LISTS = 'REMOVE_LIST_FROM_HIGHLIGHTED_LISTS'; diff --git a/app/assets/javascripts/boards/stores/mutations.js b/app/assets/javascripts/boards/stores/mutations.js index 6c79b22d308..79c98c3d90c 100644 --- a/app/assets/javascripts/boards/stores/mutations.js +++ b/app/assets/javascripts/boards/stores/mutations.js @@ -1,9 +1,9 @@ -import Vue from 'vue'; import { pull, union } from 'lodash'; +import Vue from 'vue'; +import { getIdFromGraphQLId } from '~/graphql_shared/utils'; +import { s__ } from '~/locale'; import { formatIssue, moveIssueListHelper } from '../boards_util'; import * as mutationTypes from './mutation_types'; -import { s__ } from '~/locale'; -import { getIdFromGraphQLId } from '~/graphql_shared/utils'; const notImplemented = () => { /* eslint-disable-next-line @gitlab/require-i18n-strings */ @@ -63,8 +63,8 @@ export default { state.error = s__('Boards|An error occurred while creating the list. Please try again.'); }, - [mutationTypes.RECEIVE_LABELS_FAILURE]: (state) => { - state.error = s__('Boards|An error occurred while fetching labels. Please reload the page.'); + [mutationTypes.RECEIVE_LABELS_SUCCESS]: (state, labels) => { + state.labels = labels; }, [mutationTypes.GENERATE_DEFAULT_LISTS_FAILURE]: (state) => { @@ -258,4 +258,28 @@ export default { [mutationTypes.SET_SELECTED_PROJECT]: (state, project) => { state.selectedProject = project; }, + + [mutationTypes.ADD_BOARD_ITEM_TO_SELECTION]: (state, boardItem) => { + state.selectedBoardItems = [...state.selectedBoardItems, boardItem]; + }, + + [mutationTypes.REMOVE_BOARD_ITEM_FROM_SELECTION]: (state, boardItem) => { + Vue.set( + state, + 'selectedBoardItems', + state.selectedBoardItems.filter((obj) => obj !== boardItem), + ); + }, + + [mutationTypes.SET_ADD_COLUMN_FORM_VISIBLE]: (state, visible) => { + state.addColumnFormVisible = visible; + }, + + [mutationTypes.ADD_LIST_TO_HIGHLIGHTED_LISTS]: (state, listId) => { + state.highlightedLists.push(listId); + }, + + [mutationTypes.REMOVE_LIST_FROM_HIGHLIGHTED_LISTS]: (state, listId) => { + state.highlightedLists = state.highlightedLists.filter((id) => id !== listId); + }, }; diff --git a/app/assets/javascripts/boards/stores/state.js b/app/assets/javascripts/boards/stores/state.js index aba7da373cf..91544d6c9c5 100644 --- a/app/assets/javascripts/boards/stores/state.js +++ b/app/assets/javascripts/boards/stores/state.js @@ -14,6 +14,9 @@ export default () => ({ issues: {}, filterParams: {}, boardConfig: {}, + labels: [], + highlightedLists: [], + selectedBoardItems: [], groupProjects: [], groupProjectsFlags: { isLoading: false, @@ -22,6 +25,7 @@ export default () => ({ }, selectedProject: {}, error: undefined, + addColumnFormVisible: false, // TODO: remove after ce/ee split of board_content.vue isShowingEpicsSwimlanes: false, }); diff --git a/app/assets/javascripts/boards/toggle_focus.js b/app/assets/javascripts/boards/toggle_focus.js index 347deb81846..0a230f72dcc 100644 --- a/app/assets/javascripts/boards/toggle_focus.js +++ b/app/assets/javascripts/boards/toggle_focus.js @@ -1,45 +1,17 @@ -import $ from 'jquery'; import Vue from 'vue'; -import { GlIcon } from '@gitlab/ui'; -import { hide } from '~/tooltips'; +import ToggleFocus from './components/toggle_focus.vue'; -export default (ModalStore, boardsStore) => { - const issueBoardsContent = document.querySelector('.content-wrapper > .js-focus-mode-board'); +export default () => { + const issueBoardsContentSelector = '.content-wrapper > .js-focus-mode-board'; return new Vue({ - el: document.getElementById('js-toggle-focus-btn'), - components: { - GlIcon, + el: '#js-toggle-focus-btn', + render(h) { + return h(ToggleFocus, { + props: { + issueBoardsContentSelector, + }, + }); }, - data: { - modal: ModalStore.store, - store: boardsStore.state, - isFullscreen: false, - }, - methods: { - toggleFocusMode() { - const $el = $(this.$refs.toggleFocusModeButton); - hide($el); - - issueBoardsContent.classList.toggle('is-focused'); - - this.isFullscreen = !this.isFullscreen; - }, - }, - template: ` -
- - - -
- `, }); }; -- cgit v1.2.3