2019-07-17 16:42:33 -04:00
|
|
|
<script>
|
2021-05-31 08:10:34 -04:00
|
|
|
import { GlModal, GlAlert } from '@gitlab/ui';
|
|
|
|
import { mapGetters, mapActions, mapState } from 'vuex';
|
2021-06-08 11:10:00 -04:00
|
|
|
import ListLabel from '~/boards/models/label';
|
2021-06-30 05:08:07 -04:00
|
|
|
import { TYPE_ITERATION, TYPE_MILESTONE, TYPE_USER } from '~/graphql_shared/constants';
|
2021-01-13 07:10:27 -05:00
|
|
|
import { convertToGraphQLId } from '~/graphql_shared/utils';
|
2021-02-14 13:09:20 -05:00
|
|
|
import { getParameterByName } from '~/lib/utils/common_utils';
|
|
|
|
import { visitUrl } from '~/lib/utils/url_utility';
|
|
|
|
import { __, s__ } from '~/locale';
|
2020-12-23 10:09:54 -05:00
|
|
|
import { fullLabelId, fullBoardId } from '../boards_util';
|
2021-02-08 22:09:18 -05:00
|
|
|
import { formType } from '../constants';
|
2019-07-17 16:42:33 -04:00
|
|
|
|
2020-12-23 10:09:54 -05:00
|
|
|
import createBoardMutation from '../graphql/board_create.mutation.graphql';
|
2021-01-08 10:10:26 -05:00
|
|
|
import destroyBoardMutation from '../graphql/board_destroy.mutation.graphql';
|
2021-02-14 13:09:20 -05:00
|
|
|
import updateBoardMutation from '../graphql/board_update.mutation.graphql';
|
2021-02-01 10:08:56 -05:00
|
|
|
import BoardConfigurationOptions from './board_configuration_options.vue';
|
2020-09-29 14:09:52 -04:00
|
|
|
|
2019-07-17 16:42:33 -04:00
|
|
|
const boardDefaults = {
|
|
|
|
id: false,
|
|
|
|
name: '',
|
|
|
|
labels: [],
|
|
|
|
milestone_id: undefined,
|
2020-12-17 01:10:13 -05:00
|
|
|
iteration_id: undefined,
|
2019-07-17 16:42:33 -04:00
|
|
|
assignee: {},
|
|
|
|
assignee_id: undefined,
|
|
|
|
weight: null,
|
2020-09-29 14:09:52 -04:00
|
|
|
hide_backlog_list: false,
|
|
|
|
hide_closed_list: false,
|
2019-07-17 16:42:33 -04:00
|
|
|
};
|
|
|
|
|
|
|
|
export default {
|
2020-12-07 04:09:42 -05:00
|
|
|
i18n: {
|
|
|
|
[formType.new]: { title: s__('Board|Create new board'), btnText: s__('Board|Create board') },
|
|
|
|
[formType.delete]: { title: s__('Board|Delete board'), btnText: __('Delete') },
|
|
|
|
[formType.edit]: { title: s__('Board|Edit board'), btnText: __('Save changes') },
|
|
|
|
scopeModalTitle: s__('Board|Board scope'),
|
|
|
|
cancelButtonText: __('Cancel'),
|
|
|
|
deleteErrorMessage: s__('Board|Failed to delete board. Please try again.'),
|
|
|
|
saveErrorMessage: __('Unable to save your changes. Please try again.'),
|
|
|
|
deleteConfirmationMessage: s__('Board|Are you sure you want to delete this board?'),
|
|
|
|
titleFieldLabel: __('Title'),
|
|
|
|
titleFieldPlaceholder: s__('Board|Enter board name'),
|
|
|
|
},
|
2019-07-17 16:42:33 -04:00
|
|
|
components: {
|
|
|
|
BoardScope: () => import('ee_component/boards/components/board_scope.vue'),
|
2020-12-07 04:09:42 -05:00
|
|
|
GlModal,
|
2020-09-29 14:09:52 -04:00
|
|
|
BoardConfigurationOptions,
|
2021-05-31 08:10:34 -04:00
|
|
|
GlAlert,
|
2019-07-17 16:42:33 -04:00
|
|
|
},
|
2021-01-12 07:10:49 -05:00
|
|
|
inject: {
|
|
|
|
fullPath: {
|
|
|
|
default: '',
|
|
|
|
},
|
|
|
|
rootPath: {
|
|
|
|
default: '',
|
|
|
|
},
|
|
|
|
},
|
2019-07-17 16:42:33 -04:00
|
|
|
props: {
|
|
|
|
canAdminBoard: {
|
|
|
|
type: Boolean,
|
|
|
|
required: true,
|
|
|
|
},
|
|
|
|
labelsPath: {
|
|
|
|
type: String,
|
|
|
|
required: true,
|
|
|
|
},
|
2020-09-03 14:08:29 -04:00
|
|
|
labelsWebUrl: {
|
|
|
|
type: String,
|
|
|
|
required: true,
|
|
|
|
},
|
2019-07-17 16:42:33 -04:00
|
|
|
scopedIssueBoardFeatureEnabled: {
|
|
|
|
type: Boolean,
|
|
|
|
required: false,
|
|
|
|
default: false,
|
|
|
|
},
|
|
|
|
projectId: {
|
|
|
|
type: Number,
|
|
|
|
required: false,
|
|
|
|
default: 0,
|
|
|
|
},
|
|
|
|
groupId: {
|
|
|
|
type: Number,
|
|
|
|
required: false,
|
|
|
|
default: 0,
|
|
|
|
},
|
|
|
|
weights: {
|
|
|
|
type: Array,
|
|
|
|
required: false,
|
|
|
|
default: () => [],
|
|
|
|
},
|
|
|
|
enableScopedLabels: {
|
|
|
|
type: Boolean,
|
|
|
|
required: false,
|
|
|
|
default: false,
|
|
|
|
},
|
2020-12-11 10:10:04 -05:00
|
|
|
currentBoard: {
|
|
|
|
type: Object,
|
|
|
|
required: true,
|
|
|
|
},
|
2021-02-08 22:09:18 -05:00
|
|
|
currentPage: {
|
|
|
|
type: String,
|
|
|
|
required: true,
|
|
|
|
},
|
2020-12-11 10:10:04 -05:00
|
|
|
},
|
2019-07-17 16:42:33 -04:00
|
|
|
data() {
|
|
|
|
return {
|
|
|
|
board: { ...boardDefaults, ...this.currentBoard },
|
|
|
|
isLoading: false,
|
|
|
|
};
|
|
|
|
},
|
|
|
|
computed: {
|
2021-05-31 08:10:34 -04:00
|
|
|
...mapState(['error']),
|
2021-03-23 11:09:28 -04:00
|
|
|
...mapGetters(['isIssueBoard', 'isGroupBoard', 'isProjectBoard']),
|
2019-07-17 16:42:33 -04:00
|
|
|
isNewForm() {
|
2020-12-07 04:09:42 -05:00
|
|
|
return this.currentPage === formType.new;
|
2019-07-17 16:42:33 -04:00
|
|
|
},
|
|
|
|
isDeleteForm() {
|
2020-12-07 04:09:42 -05:00
|
|
|
return this.currentPage === formType.delete;
|
2019-07-17 16:42:33 -04:00
|
|
|
},
|
|
|
|
isEditForm() {
|
2020-12-07 04:09:42 -05:00
|
|
|
return this.currentPage === formType.edit;
|
2019-07-17 16:42:33 -04:00
|
|
|
},
|
|
|
|
buttonText() {
|
2020-12-07 04:09:42 -05:00
|
|
|
return this.$options.i18n[this.currentPage].btnText;
|
2019-07-17 16:42:33 -04:00
|
|
|
},
|
|
|
|
buttonKind() {
|
|
|
|
if (this.isNewForm) {
|
|
|
|
return 'success';
|
|
|
|
}
|
|
|
|
if (this.isDeleteForm) {
|
|
|
|
return 'danger';
|
|
|
|
}
|
2021-04-06 05:09:03 -04:00
|
|
|
return 'confirm';
|
2019-07-17 16:42:33 -04:00
|
|
|
},
|
|
|
|
title() {
|
|
|
|
if (this.readonly) {
|
2020-12-07 04:09:42 -05:00
|
|
|
return this.$options.i18n.scopeModalTitle;
|
2019-07-17 16:42:33 -04:00
|
|
|
}
|
2020-12-07 04:09:42 -05:00
|
|
|
|
|
|
|
return this.$options.i18n[this.currentPage].title;
|
2019-07-17 16:42:33 -04:00
|
|
|
},
|
|
|
|
readonly() {
|
|
|
|
return !this.canAdminBoard;
|
|
|
|
},
|
|
|
|
submitDisabled() {
|
|
|
|
return this.isLoading || this.board.name.length === 0;
|
|
|
|
},
|
2020-12-07 04:09:42 -05:00
|
|
|
primaryProps() {
|
|
|
|
return {
|
|
|
|
text: this.buttonText,
|
|
|
|
attributes: [
|
|
|
|
{
|
|
|
|
variant: this.buttonKind,
|
|
|
|
disabled: this.submitDisabled,
|
|
|
|
loading: this.isLoading,
|
|
|
|
'data-qa-selector': 'save_changes_button',
|
|
|
|
},
|
|
|
|
],
|
|
|
|
};
|
|
|
|
},
|
|
|
|
cancelProps() {
|
|
|
|
return {
|
|
|
|
text: this.$options.i18n.cancelButtonText,
|
|
|
|
};
|
|
|
|
},
|
2020-12-23 10:09:54 -05:00
|
|
|
currentMutation() {
|
|
|
|
return this.board.id ? updateBoardMutation : createBoardMutation;
|
|
|
|
},
|
2021-03-26 08:09:15 -04:00
|
|
|
deleteMutation() {
|
|
|
|
return destroyBoardMutation;
|
|
|
|
},
|
2021-02-23 04:10:45 -05:00
|
|
|
baseMutationVariables() {
|
2020-12-23 10:09:54 -05:00
|
|
|
const { board } = this;
|
2021-02-23 04:10:45 -05:00
|
|
|
const variables = {
|
2020-12-23 10:09:54 -05:00
|
|
|
name: board.name,
|
|
|
|
hideBacklogList: board.hide_backlog_list,
|
|
|
|
hideClosedList: board.hide_closed_list,
|
2020-12-11 10:10:04 -05:00
|
|
|
};
|
2020-12-24 04:10:15 -05:00
|
|
|
|
2020-12-23 10:09:54 -05:00
|
|
|
return board.id
|
|
|
|
? {
|
2021-02-23 04:10:45 -05:00
|
|
|
...variables,
|
2020-12-23 10:09:54 -05:00
|
|
|
id: fullBoardId(board.id),
|
|
|
|
}
|
|
|
|
: {
|
2021-02-23 04:10:45 -05:00
|
|
|
...variables,
|
2021-03-10 04:09:29 -05:00
|
|
|
projectPath: this.isProjectBoard ? this.fullPath : undefined,
|
|
|
|
groupPath: this.isGroupBoard ? this.fullPath : undefined,
|
2020-12-23 10:09:54 -05:00
|
|
|
};
|
2020-12-11 10:10:04 -05:00
|
|
|
},
|
2021-03-23 11:09:28 -04:00
|
|
|
issueBoardScopeMutationVariables() {
|
2021-02-23 04:10:45 -05:00
|
|
|
return {
|
|
|
|
weight: this.board.weight,
|
|
|
|
assigneeId: this.board.assignee?.id
|
2021-06-30 05:08:07 -04:00
|
|
|
? convertToGraphQLId(TYPE_USER, this.board.assignee.id)
|
2021-02-23 04:10:45 -05:00
|
|
|
: null,
|
|
|
|
milestoneId:
|
|
|
|
this.board.milestone?.id || this.board.milestone?.id === 0
|
2021-06-30 05:08:07 -04:00
|
|
|
? convertToGraphQLId(TYPE_MILESTONE, this.board.milestone.id)
|
2021-02-23 04:10:45 -05:00
|
|
|
: null,
|
|
|
|
iterationId: this.board.iteration_id
|
2021-06-30 05:08:07 -04:00
|
|
|
? convertToGraphQLId(TYPE_ITERATION, this.board.iteration_id)
|
2021-02-23 04:10:45 -05:00
|
|
|
: null,
|
|
|
|
};
|
|
|
|
},
|
2021-03-23 11:09:28 -04:00
|
|
|
boardScopeMutationVariables() {
|
|
|
|
return {
|
|
|
|
labelIds: this.board.labels.map(fullLabelId),
|
|
|
|
...(this.isIssueBoard && this.issueBoardScopeMutationVariables),
|
|
|
|
};
|
|
|
|
},
|
2021-02-23 04:10:45 -05:00
|
|
|
mutationVariables() {
|
|
|
|
return {
|
|
|
|
...this.baseMutationVariables,
|
|
|
|
...(this.scopedIssueBoardFeatureEnabled ? this.boardScopeMutationVariables : {}),
|
|
|
|
};
|
|
|
|
},
|
2019-07-17 16:42:33 -04:00
|
|
|
},
|
|
|
|
mounted() {
|
|
|
|
this.resetFormState();
|
|
|
|
if (this.$refs.name) {
|
|
|
|
this.$refs.name.focus();
|
|
|
|
}
|
|
|
|
},
|
|
|
|
methods: {
|
2021-05-31 08:10:34 -04:00
|
|
|
...mapActions(['setError', 'unsetError']),
|
2021-02-23 04:10:45 -05:00
|
|
|
boardCreateResponse(data) {
|
|
|
|
return data.createBoard.board.webPath;
|
|
|
|
},
|
|
|
|
boardUpdateResponse(data) {
|
|
|
|
const path = data.updateBoard.board.webPath;
|
|
|
|
const param = getParameterByName('group_by')
|
|
|
|
? `?group_by=${getParameterByName('group_by')}`
|
|
|
|
: '';
|
|
|
|
return `${path}${param}`;
|
|
|
|
},
|
2021-06-08 11:10:00 -04:00
|
|
|
cancel() {
|
|
|
|
this.$emit('cancel');
|
|
|
|
},
|
2020-12-23 10:09:54 -05:00
|
|
|
async createOrUpdateBoard() {
|
|
|
|
const response = await this.$apollo.mutate({
|
|
|
|
mutation: this.currentMutation,
|
|
|
|
variables: { input: this.mutationVariables },
|
2020-12-11 10:10:04 -05:00
|
|
|
});
|
|
|
|
|
2021-01-13 07:10:27 -05:00
|
|
|
if (!this.board.id) {
|
2021-02-23 04:10:45 -05:00
|
|
|
return this.boardCreateResponse(response.data);
|
2021-01-13 07:10:27 -05:00
|
|
|
}
|
|
|
|
|
2021-02-23 04:10:45 -05:00
|
|
|
return this.boardUpdateResponse(response.data);
|
2020-12-11 10:10:04 -05:00
|
|
|
},
|
2021-03-26 08:09:15 -04:00
|
|
|
async deleteBoard() {
|
|
|
|
await this.$apollo.mutate({
|
|
|
|
mutation: this.deleteMutation,
|
|
|
|
variables: {
|
|
|
|
id: fullBoardId(this.board.id),
|
|
|
|
},
|
|
|
|
});
|
|
|
|
},
|
2020-12-23 10:09:54 -05:00
|
|
|
async submit() {
|
2019-07-17 16:42:33 -04:00
|
|
|
if (this.board.name.length === 0) return;
|
|
|
|
this.isLoading = true;
|
|
|
|
if (this.isDeleteForm) {
|
2020-12-23 10:09:54 -05:00
|
|
|
try {
|
2021-03-26 08:09:15 -04:00
|
|
|
await this.deleteBoard();
|
2021-01-08 10:10:26 -05:00
|
|
|
visitUrl(this.rootPath);
|
2020-12-23 10:09:54 -05:00
|
|
|
} catch {
|
2021-05-31 08:10:34 -04:00
|
|
|
this.setError({ message: this.$options.i18n.deleteErrorMessage });
|
2020-12-23 10:09:54 -05:00
|
|
|
} finally {
|
|
|
|
this.isLoading = false;
|
|
|
|
}
|
2019-07-17 16:42:33 -04:00
|
|
|
} else {
|
2020-12-23 10:09:54 -05:00
|
|
|
try {
|
2021-01-13 07:10:27 -05:00
|
|
|
const url = await this.createOrUpdateBoard();
|
2020-12-23 10:09:54 -05:00
|
|
|
visitUrl(url);
|
|
|
|
} catch {
|
2021-05-31 08:10:34 -04:00
|
|
|
this.setError({ message: this.$options.i18n.saveErrorMessage });
|
2020-12-23 10:09:54 -05:00
|
|
|
} finally {
|
|
|
|
this.isLoading = false;
|
|
|
|
}
|
2019-07-17 16:42:33 -04:00
|
|
|
}
|
|
|
|
},
|
|
|
|
resetFormState() {
|
|
|
|
if (this.isNewForm) {
|
|
|
|
// Clear the form when we open the "New board" modal
|
|
|
|
this.board = { ...boardDefaults };
|
|
|
|
} else if (this.currentBoard && Object.keys(this.currentBoard).length) {
|
|
|
|
this.board = { ...boardDefaults, ...this.currentBoard };
|
|
|
|
}
|
|
|
|
},
|
2021-06-08 11:10:00 -04:00
|
|
|
setIteration(iterationId) {
|
|
|
|
this.board.iteration_id = iterationId;
|
|
|
|
},
|
|
|
|
setBoardLabels(labels) {
|
|
|
|
labels.forEach((label) => {
|
|
|
|
if (label.set && !this.board.labels.find((l) => l.id === label.id)) {
|
|
|
|
this.board.labels.push(
|
|
|
|
new ListLabel({
|
|
|
|
id: label.id,
|
|
|
|
title: label.title,
|
|
|
|
color: label.color,
|
|
|
|
textColor: label.text_color,
|
|
|
|
}),
|
|
|
|
);
|
|
|
|
} else if (!label.set) {
|
|
|
|
this.board.labels = this.board.labels.filter((selected) => selected.id !== label.id);
|
|
|
|
}
|
|
|
|
});
|
|
|
|
},
|
2019-07-17 16:42:33 -04:00
|
|
|
},
|
|
|
|
};
|
|
|
|
</script>
|
|
|
|
|
|
|
|
<template>
|
2020-12-07 04:09:42 -05:00
|
|
|
<gl-modal
|
|
|
|
modal-id="board-config-modal"
|
|
|
|
modal-class="board-config-modal"
|
|
|
|
content-class="gl-absolute gl-top-7"
|
|
|
|
visible
|
2019-07-17 16:42:33 -04:00
|
|
|
:hide-footer="readonly"
|
|
|
|
:title="title"
|
2020-12-07 04:09:42 -05:00
|
|
|
:action-primary="primaryProps"
|
|
|
|
:action-cancel="cancelProps"
|
|
|
|
@primary="submit"
|
2019-07-17 16:42:33 -04:00
|
|
|
@cancel="cancel"
|
2020-12-07 04:09:42 -05:00
|
|
|
@close="cancel"
|
|
|
|
@hide.prevent
|
2019-07-17 16:42:33 -04:00
|
|
|
>
|
2021-05-31 08:10:34 -04:00
|
|
|
<gl-alert
|
|
|
|
v-if="error"
|
|
|
|
class="gl-mb-3"
|
|
|
|
variant="danger"
|
|
|
|
:dismissible="true"
|
|
|
|
@dismiss="unsetError"
|
|
|
|
>
|
|
|
|
{{ error }}
|
|
|
|
</gl-alert>
|
2020-12-11 10:10:04 -05:00
|
|
|
<p v-if="isDeleteForm" data-testid="delete-confirmation-message">
|
|
|
|
{{ $options.i18n.deleteConfirmationMessage }}
|
|
|
|
</p>
|
|
|
|
<form v-else class="js-board-config-modal" data-testid="board-form-wrapper" @submit.prevent>
|
|
|
|
<div v-if="!readonly" class="gl-mb-5" data-testid="board-form">
|
2020-12-07 04:09:42 -05:00
|
|
|
<label class="gl-font-weight-bold gl-font-lg" for="board-new-name">
|
|
|
|
{{ $options.i18n.titleFieldLabel }}
|
|
|
|
</label>
|
|
|
|
<input
|
|
|
|
id="board-new-name"
|
|
|
|
ref="name"
|
|
|
|
v-model="board.name"
|
|
|
|
class="form-control"
|
|
|
|
data-qa-selector="board_name_field"
|
|
|
|
type="text"
|
|
|
|
:placeholder="$options.i18n.titleFieldPlaceholder"
|
|
|
|
@keyup.enter="submit"
|
2020-09-29 14:09:52 -04:00
|
|
|
/>
|
2020-12-07 04:09:42 -05:00
|
|
|
</div>
|
2020-09-29 14:09:52 -04:00
|
|
|
|
2020-12-07 04:09:42 -05:00
|
|
|
<board-configuration-options
|
2020-12-23 10:09:54 -05:00
|
|
|
:hide-backlog-list.sync="board.hide_backlog_list"
|
|
|
|
:hide-closed-list.sync="board.hide_closed_list"
|
2021-01-22 04:08:53 -05:00
|
|
|
:readonly="readonly"
|
2020-12-07 04:09:42 -05:00
|
|
|
/>
|
|
|
|
|
|
|
|
<board-scope
|
2021-03-23 11:09:28 -04:00
|
|
|
v-if="scopedIssueBoardFeatureEnabled"
|
2020-12-07 04:09:42 -05:00
|
|
|
:collapse-scope="isNewForm"
|
|
|
|
:board="board"
|
|
|
|
:can-admin-board="canAdminBoard"
|
|
|
|
:labels-path="labelsPath"
|
|
|
|
:labels-web-url="labelsWebUrl"
|
|
|
|
:enable-scoped-labels="enableScopedLabels"
|
|
|
|
:project-id="projectId"
|
|
|
|
:group-id="groupId"
|
|
|
|
:weights="weights"
|
2020-12-17 01:10:13 -05:00
|
|
|
@set-iteration="setIteration"
|
2021-06-08 11:10:00 -04:00
|
|
|
@set-board-labels="setBoardLabels"
|
2020-12-07 04:09:42 -05:00
|
|
|
/>
|
|
|
|
</form>
|
|
|
|
</gl-modal>
|
2019-07-17 16:42:33 -04:00
|
|
|
</template>
|