diff options
Diffstat (limited to 'app/assets/javascripts/boards/stores')
-rw-r--r-- | app/assets/javascripts/boards/stores/actions.js | 193 | ||||
-rw-r--r-- | app/assets/javascripts/boards/stores/boards_store.js | 7 | ||||
-rw-r--r-- | app/assets/javascripts/boards/stores/getters.js | 11 | ||||
-rw-r--r-- | app/assets/javascripts/boards/stores/mutation_types.js | 9 | ||||
-rw-r--r-- | app/assets/javascripts/boards/stores/mutations.js | 41 |
5 files changed, 214 insertions, 47 deletions
diff --git a/app/assets/javascripts/boards/stores/actions.js b/app/assets/javascripts/boards/stores/actions.js index 1fed1228106..dd950a45076 100644 --- a/app/assets/javascripts/boards/stores/actions.js +++ b/app/assets/javascripts/boards/stores/actions.js @@ -1,26 +1,30 @@ -import Cookies from 'js-cookie'; import { pick } from 'lodash'; import boardListsQuery from 'ee_else_ce/boards/queries/board_lists.query.graphql'; -import { __ } from '~/locale'; -import { parseBoolean } from '~/lib/utils/common_utils'; import createGqClient, { fetchPolicies } from '~/lib/graphql'; import { getIdFromGraphQLId } from '~/graphql_shared/utils'; -import { BoardType, ListType, inactiveId } from '~/boards/constants'; +import { BoardType, ListType, inactiveId, DEFAULT_LABELS } from '~/boards/constants'; import * as types from './mutation_types'; import { formatBoardLists, formatListIssues, fullBoardId, formatListsPageInfo, + formatIssue, } from '../boards_util'; import boardStore from '~/boards/stores/boards_store'; +import updateAssignees from '~/vue_shared/components/sidebar/queries/updateAssignees.mutation.graphql'; import listsIssuesQuery from '../queries/lists_issues.query.graphql'; +import boardLabelsQuery from '../queries/board_labels.query.graphql'; import createBoardListMutation from '../queries/board_list_create.mutation.graphql'; import updateBoardListMutation from '../queries/board_list_update.mutation.graphql'; import issueMoveListMutation from '../queries/issue_move_list.mutation.graphql'; +import destroyBoardListMutation from '../queries/board_list_destroy.mutation.graphql'; +import issueCreateMutation from '../queries/issue_create.mutation.graphql'; import issueSetLabels from '../queries/issue_set_labels.mutation.graphql'; +import issueSetDueDate from '../queries/issue_set_due_date.mutation.graphql'; +import issueSetSubscriptionMutation from '../graphql/mutations/issue_set_subscription.mutation.graphql'; const notImplemented = () => { /* eslint-disable-next-line @gitlab/require-i18n-strings */ @@ -83,7 +87,7 @@ export default { if (!lists.nodes.find(l => l.listType === ListType.backlog) && !hideBacklogList) { dispatch('createList', { backlog: true }); } - dispatch('showWelcomeList'); + dispatch('generateDefaultLists'); }) .catch(() => commit(types.RECEIVE_BOARD_LISTS_FAILURE)); }, @@ -121,7 +125,32 @@ export default { ); }, - showWelcomeList: ({ state, dispatch }) => { + showPromotionList: () => {}, + + fetchLabels: ({ state, commit }, searchTerm) => { + const { endpoints, boardType } = state; + const { fullPath } = endpoints; + + const variables = { + fullPath, + searchTerm, + isGroup: boardType === BoardType.group, + isProject: boardType === BoardType.project, + }; + + return gqlClient + .query({ + query: boardLabelsQuery, + variables, + }) + .then(({ data }) => { + const labels = data[boardType]?.labels; + return labels.nodes; + }) + .catch(() => commit(types.RECEIVE_LABELS_FAILURE)); + }, + + generateDefaultLists: async ({ state, commit, dispatch }) => { if (state.disabled) { return; } @@ -132,22 +161,18 @@ export default { ) { return; } - if (parseBoolean(Cookies.get('issue_board_welcome_hidden'))) { - return; - } - dispatch('addList', { - id: 'blank', - listType: ListType.blank, - title: __('Welcome to your issue board!'), - position: 0, - }); - }, - - showPromotionList: () => {}, + const fetchLabelsAndCreateList = label => { + return dispatch('fetchLabels', label) + .then(res => { + if (res.length > 0) { + dispatch('createList', { labelId: res[0].id }); + } + }) + .catch(() => commit(types.GENERATE_DEFAULT_LISTS_FAILURE)); + }; - generateDefaultLists: () => { - notImplemented(); + await Promise.all(DEFAULT_LABELS.map(fetchLabelsAndCreateList)); }, moveList: ( @@ -191,8 +216,26 @@ export default { }); }, - deleteList: () => { - notImplemented(); + removeList: ({ state, commit }, listId) => { + const listsBackup = { ...state.boardLists }; + + commit(types.REMOVE_LIST, listId); + + return gqlClient + .mutate({ + mutation: destroyBoardListMutation, + variables: { + listId, + }, + }) + .then(({ data: { destroyBoardList: { errors } } }) => { + if (errors.length > 0) { + commit(types.REMOVE_LIST_FAILURE, listsBackup); + } + }) + .catch(() => { + commit(types.REMOVE_LIST_FAILURE, listsBackup); + }); }, fetchIssuesForList: ({ state, commit }, { listId, fetchNext = false }) => { @@ -271,20 +314,69 @@ export default { ); }, - createNewIssue: () => { - notImplemented(); + setAssignees: ({ commit, getters }, assigneeUsernames) => { + return gqlClient + .mutate({ + mutation: updateAssignees, + variables: { + iid: getters.activeIssue.iid, + projectPath: getters.activeIssue.referencePath.split('#')[0], + assigneeUsernames, + }, + }) + .then(({ data }) => { + commit('UPDATE_ISSUE_BY_ID', { + issueId: getters.activeIssue.id, + prop: 'assignees', + value: data.issueSetAssignees.issue.assignees.nodes, + }); + }); + }, + + createNewIssue: ({ commit, state }, issueInput) => { + const input = issueInput; + const { boardType, endpoints } = state; + if (boardType === BoardType.project) { + input.projectPath = endpoints.fullPath; + } + + return gqlClient + .mutate({ + mutation: issueCreateMutation, + variables: { input }, + }) + .then(({ data }) => { + if (data.createIssue.errors.length) { + commit(types.CREATE_ISSUE_FAILURE); + } else { + return data.createIssue?.issue; + } + return null; + }) + .catch(() => commit(types.CREATE_ISSUE_FAILURE)); }, addListIssue: ({ commit }, { list, issue, position }) => { commit(types.ADD_ISSUE_TO_LIST, { list, issue, position }); }, - addListIssueFailure: ({ commit }, { list, issue }) => { - commit(types.ADD_ISSUE_TO_LIST_FAILURE, { list, issue }); + addListNewIssue: ({ commit, dispatch }, { issueInput, list }) => { + const issue = formatIssue({ ...issueInput, id: 'tmp' }); + commit(types.ADD_ISSUE_TO_LIST, { list, issue, position: 0 }); + + dispatch('createNewIssue', issueInput) + .then(res => { + commit(types.ADD_ISSUE_TO_LIST, { + list, + issue: formatIssue({ ...res, id: getIdFromGraphQLId(res.id) }), + }); + commit(types.REMOVE_ISSUE_FROM_LIST, { list, issue }); + }) + .catch(() => commit(types.ADD_ISSUE_TO_LIST_FAILURE, { list, issueId: issueInput.id })); }, setActiveIssueLabels: async ({ commit, getters }, input) => { - const activeIssue = getters.getActiveIssue; + const { activeIssue } = getters; const { data } = await gqlClient.mutate({ mutation: issueSetLabels, variables: { @@ -308,6 +400,53 @@ export default { }); }, + setActiveIssueDueDate: async ({ commit, getters }, input) => { + const { activeIssue } = getters; + const { data } = await gqlClient.mutate({ + mutation: issueSetDueDate, + variables: { + input: { + iid: String(activeIssue.iid), + projectPath: input.projectPath, + dueDate: input.dueDate, + }, + }, + }); + + if (data.updateIssue?.errors?.length > 0) { + throw new Error(data.updateIssue.errors); + } + + commit(types.UPDATE_ISSUE_BY_ID, { + issueId: activeIssue.id, + prop: 'dueDate', + value: data.updateIssue.issue.dueDate, + }); + }, + + setActiveIssueSubscribed: async ({ commit, getters }, input) => { + const { data } = await gqlClient.mutate({ + mutation: issueSetSubscriptionMutation, + variables: { + input: { + iid: String(getters.activeIssue.iid), + projectPath: input.projectPath, + subscribedState: input.subscribed, + }, + }, + }); + + if (data.issueSetSubscription?.errors?.length > 0) { + throw new Error(data.issueSetSubscription.errors); + } + + commit(types.UPDATE_ISSUE_BY_ID, { + issueId: getters.activeIssue.id, + prop: 'subscribed', + value: data.issueSetSubscription.issue.subscribed, + }); + }, + fetchBacklog: () => { notImplemented(); }, diff --git a/app/assets/javascripts/boards/stores/boards_store.js b/app/assets/javascripts/boards/stores/boards_store.js index d1a5db1bcc5..337b2897fe9 100644 --- a/app/assets/javascripts/boards/stores/boards_store.js +++ b/app/assets/javascripts/boards/stores/boards_store.js @@ -1,7 +1,6 @@ /* eslint-disable no-shadow, no-param-reassign,consistent-return */ /* global List */ /* global ListIssue */ -import $ from 'jquery'; import { sortBy, pick } from 'lodash'; import Vue from 'vue'; import Cookies from 'js-cookie'; @@ -119,8 +118,12 @@ const boardsStore = { // https://gitlab.com/gitlab-org/gitlab-foss/issues/30821 }); }, + updateNewListDropdown(listId) { - $(`.js-board-list-${listId}`).removeClass('is-active'); + // eslint-disable-next-line no-unused-expressions + document + .querySelector(`.js-board-list-${getIdFromGraphQLId(listId)}`) + ?.classList.remove('is-active'); }, shouldAddBlankState() { // Decide whether to add the blank state diff --git a/app/assets/javascripts/boards/stores/getters.js b/app/assets/javascripts/boards/stores/getters.js index 89a3b14b262..cd28b4a0ff7 100644 --- a/app/assets/javascripts/boards/stores/getters.js +++ b/app/assets/javascripts/boards/stores/getters.js @@ -2,7 +2,7 @@ import { find } from 'lodash'; import { inactiveId } from '../constants'; export default { - getLabelToggleState: state => (state.isShowingLabels ? 'on' : 'off'), + labelToggleState: state => (state.isShowingLabels ? 'on' : 'off'), isSidebarOpen: state => state.activeId !== inactiveId, isSwimlanesOn: state => { if (!gon?.features?.boardsWithSwimlanes && !gon?.features?.swimlanes) { @@ -15,15 +15,20 @@ export default { return state.issues[id] || {}; }, - getIssues: (state, getters) => listId => { + getIssuesByList: (state, getters) => listId => { const listIssueIds = state.issuesByListId[listId] || []; return listIssueIds.map(id => getters.getIssueById(id)); }, - getActiveIssue: state => { + activeIssue: state => { return state.issues[state.activeId] || {}; }, + projectPathForActiveIssue: (_, getters) => { + const referencePath = getters.activeIssue.referencePath || ''; + return referencePath.slice(0, referencePath.indexOf('#')); + }, + getListByLabelId: state => labelId => { return find(state.boardLists, l => l.label?.id === labelId); }, diff --git a/app/assets/javascripts/boards/stores/mutation_types.js b/app/assets/javascripts/boards/stores/mutation_types.js index 09ab08062df..3a57cb9b5e1 100644 --- a/app/assets/javascripts/boards/stores/mutation_types.js +++ b/app/assets/javascripts/boards/stores/mutation_types.js @@ -2,6 +2,8 @@ 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 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'; export const SHOW_PROMOTION_LIST = 'SHOW_PROMOTION_LIST'; @@ -10,12 +12,12 @@ export const RECEIVE_ADD_LIST_SUCCESS = 'RECEIVE_ADD_LIST_SUCCESS'; export const RECEIVE_ADD_LIST_ERROR = 'RECEIVE_ADD_LIST_ERROR'; export const MOVE_LIST = 'MOVE_LIST'; export const UPDATE_LIST_FAILURE = 'UPDATE_LIST_FAILURE'; -export const REQUEST_REMOVE_LIST = 'REQUEST_REMOVE_LIST'; -export const RECEIVE_REMOVE_LIST_SUCCESS = 'RECEIVE_REMOVE_LIST_SUCCESS'; -export const RECEIVE_REMOVE_LIST_ERROR = 'RECEIVE_REMOVE_LIST_ERROR'; +export const REMOVE_LIST = 'REMOVE_LIST'; +export const REMOVE_LIST_FAILURE = 'REMOVE_LIST_FAILURE'; export const REQUEST_ISSUES_FOR_LIST = 'REQUEST_ISSUES_FOR_LIST'; export const RECEIVE_ISSUES_FOR_LIST_FAILURE = 'RECEIVE_ISSUES_FOR_LIST_FAILURE'; export const RECEIVE_ISSUES_FOR_LIST_SUCCESS = 'RECEIVE_ISSUES_FOR_LIST_SUCCESS'; +export const CREATE_ISSUE_FAILURE = 'CREATE_ISSUE_FAILURE'; export const REQUEST_ADD_ISSUE = 'REQUEST_ADD_ISSUE'; export const RECEIVE_ADD_ISSUE_SUCCESS = 'RECEIVE_ADD_ISSUE_SUCCESS'; export const RECEIVE_ADD_ISSUE_ERROR = 'RECEIVE_ADD_ISSUE_ERROR'; @@ -27,6 +29,7 @@ export const RECEIVE_UPDATE_ISSUE_SUCCESS = 'RECEIVE_UPDATE_ISSUE_SUCCESS'; export const RECEIVE_UPDATE_ISSUE_ERROR = 'RECEIVE_UPDATE_ISSUE_ERROR'; export const ADD_ISSUE_TO_LIST = 'ADD_ISSUE_TO_LIST'; export const ADD_ISSUE_TO_LIST_FAILURE = 'ADD_ISSUE_TO_LIST_FAILURE'; +export const REMOVE_ISSUE_FROM_LIST = 'REMOVE_ISSUE_FROM_LIST'; export const SET_CURRENT_PAGE = 'SET_CURRENT_PAGE'; export const TOGGLE_EMPTY_STATE = 'TOGGLE_EMPTY_STATE'; export const SET_ACTIVE_ID = 'SET_ACTIVE_ID'; diff --git a/app/assets/javascripts/boards/stores/mutations.js b/app/assets/javascripts/boards/stores/mutations.js index 0c7dbc0d2ef..bb083158c8f 100644 --- a/app/assets/javascripts/boards/stores/mutations.js +++ b/app/assets/javascripts/boards/stores/mutations.js @@ -62,6 +62,14 @@ 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.GENERATE_DEFAULT_LISTS_FAILURE]: state => { + state.error = s__('Boards|An error occurred while generating lists. Please reload the page.'); + }, + [mutationTypes.REQUEST_ADD_LIST]: () => { notImplemented(); }, @@ -85,16 +93,13 @@ export default { Vue.set(state, 'boardLists', backupList); }, - [mutationTypes.REQUEST_REMOVE_LIST]: () => { - notImplemented(); + [mutationTypes.REMOVE_LIST]: (state, listId) => { + Vue.delete(state.boardLists, listId); }, - [mutationTypes.RECEIVE_REMOVE_LIST_SUCCESS]: () => { - notImplemented(); - }, - - [mutationTypes.RECEIVE_REMOVE_LIST_ERROR]: () => { - notImplemented(); + [mutationTypes.REMOVE_LIST_FAILURE](state, listsBackup) { + state.error = s__('Boards|An error occurred while removing the list. Please try again.'); + state.boardLists = listsBackup; }, [mutationTypes.REQUEST_ISSUES_FOR_LIST]: (state, { listId, fetchNext }) => { @@ -196,16 +201,28 @@ export default { notImplemented(); }, + [mutationTypes.CREATE_ISSUE_FAILURE]: state => { + state.error = s__('Boards|An error occurred while creating the issue. Please try again.'); + }, + [mutationTypes.ADD_ISSUE_TO_LIST]: (state, { list, issue, position }) => { - const listIssues = state.issuesByListId[list.id]; - listIssues.splice(position, 0, issue.id); - Vue.set(state.issuesByListId, list.id, listIssues); + addIssueToList({ + state, + listId: list.id, + issueId: issue.id, + atIndex: position, + }); Vue.set(state.issues, issue.id, issue); }, - [mutationTypes.ADD_ISSUE_TO_LIST_FAILURE]: (state, { list, issue }) => { + [mutationTypes.ADD_ISSUE_TO_LIST_FAILURE]: (state, { list, issueId }) => { state.error = s__('Boards|An error occurred while creating the issue. Please try again.'); + removeIssueFromList({ state, listId: list.id, issueId }); + }, + + [mutationTypes.REMOVE_ISSUE_FROM_LIST]: (state, { list, issue }) => { removeIssueFromList({ state, listId: list.id, issueId: issue.id }); + Vue.delete(state.issues, issue.id); }, [mutationTypes.SET_CURRENT_PAGE]: () => { |