Merge pull request #4643 from mfilser/move_copy_card_dialog_remember_last_selected_board

Move and copy card dialog remember last selected board
This commit is contained in:
Lauri Ojansivu 2022-08-18 14:50:20 +03:00 committed by GitHub
commit 0553e36bfb
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
3 changed files with 189 additions and 135 deletions

View file

@ -712,21 +712,21 @@ template(name="exportCardPopup")
| {{_ 'export-card-pdf'}}
template(name="moveCardPopup")
+boardsAndLists
+copyAndMoveCard
template(name="copyCardPopup")
label(for='copy-card-title') {{_ 'title'}}:
textarea#copy-card-title.minicard-composer-textarea.js-card-title(autofocus)
= getTitle
+boardsAndLists
+copyAndMoveCard
template(name="copyChecklistToManyCardsPopup")
label(for='copy-checklist-cards-title') {{_ 'copyChecklistToManyCardsPopup-instructions'}}:
textarea#copy-card-title.minicard-composer-textarea.js-card-title(autofocus)
| {{_ 'copyChecklistToManyCardsPopup-format'}}
+boardsAndLists
+copyAndMoveCard
template(name="boardsAndLists")
template(name="copyAndMoveCard")
unless currentUser.isWorker
label {{_ 'boards'}}:
select.js-select-boards(autofocus)
@ -734,17 +734,17 @@ template(name="boardsAndLists")
if $eq _id currentBoard._id
option(value="{{_id}}" selected) {{_ 'current'}}
else
option(value="{{_id}}" selected="{{#if isMoveAndCopyDialogOptionBoardId _id}}selected{{/if}}") {{title}}
option(value="{{_id}}" selected="{{#if isCardDialogOptionBoardId _id}}selected{{/if}}") {{title}}
label {{_ 'swimlanes'}}:
select.js-select-swimlanes
each swimlanes
option(value="{{_id}}" selected="{{#if isMoveAndCopyDialogOptionSwimlaneId _id}}selected{{/if}}") {{title}}
option(value="{{_id}}" selected="{{#if isCardDialogOptionSwimlaneId _id}}selected{{/if}}") {{title}}
label {{_ 'lists'}}:
select.js-select-lists
each aBoardLists
option(value="{{_id}}" selected="{{#if isMoveAndCopyDialogOptionListId _id}}selected{{/if}}") {{title}}
each lists
option(value="{{_id}}" selected="{{#if isCardDialogOptionListId _id}}selected{{/if}}") {{title}}
.edit-controls.clearfix
button.primary.confirm.js-done {{_ 'done'}}

View file

@ -826,110 +826,152 @@ Template.editCardAssignerForm.events({
},
});
Template.moveCardPopup.events({
'click .js-done'() {
const card = Cards.findOne(this._id);
const bSelect = $('.js-select-boards')[0];
let boardId;
// if we are a worker, we won't have a board select so we just use the
// current boardId of the card.
if (bSelect) {
boardId = bSelect.options[bSelect.selectedIndex].value;
} else {
boardId = card.boardId;
}
const lSelect = $('.js-select-lists')[0];
const listId = lSelect.options[lSelect.selectedIndex].value;
const slSelect = $('.js-select-swimlanes')[0];
const swimlaneId = slSelect.options[slSelect.selectedIndex].value;
class DialogWithBoardSwimlaneList extends BlazeComponent {
/** returns the card dialog options
* @return Object with properties { boardId, swimlaneId, listId }
*/
getCardDialogOptions() {
}
const minOrder = card.getMinSort(listId, swimlaneId);
card.move(boardId, swimlaneId, listId, minOrder - 1);
/** list is done
* @param listId the selected list id
* @param options the selected options (Object with properties { boardId, swimlaneId, listId })
*/
setDone(listId, options) {
}
// set new id's to card object in case the card is moved to top by the comment "moveCard" after this command (.js-move-card)
this.boardId = boardId;
this.swimlaneId = swimlaneId;
this.listId = listId;
Popup.back(2);
},
});
BlazeComponent.extendComponent({
onCreated() {
this.currentBoardId = Utils.getCurrentBoardId();
this.selectedBoardId = new ReactiveVar(this.currentBoardId);
this.setMoveAndCopyDialogOption(this.currentBoardId);
},
this.selectedSwimlaneId = new ReactiveVar('');
this.selectedListId = new ReactiveVar('');
this.setCardDialogOption(this.currentBoardId);
}
/** set the last confirmed dialog field values
* @param boardId the current board id
*/
setMoveAndCopyDialogOption(boardId) {
this.moveAndCopyDialogOption = {
'boardId': "",
'swimlaneId': "",
'listId': "",
setCardDialogOption(boardId) {
this.cardDialogOption = {
'boardId' : "",
'swimlaneId' : "",
'listId' : "",
}
let currentOptions = Meteor.user().getMoveAndCopyDialogOptions();
let currentOptions = this.getCardDialogOptions();
if (currentOptions && boardId && currentOptions[boardId]) {
this.moveAndCopyDialogOption = currentOptions[boardId];
if (this.moveAndCopyDialogOption.boardId) {
this.selectedBoardId.set(this.moveAndCopyDialogOption.boardId)
this.cardDialogOption = currentOptions[boardId];
if (this.cardDialogOption.boardId &&
this.cardDialogOption.swimlaneId &&
this.cardDialogOption.listId
)
{
this.selectedBoardId.set(this.cardDialogOption.boardId)
this.selectedSwimlaneId.set(this.cardDialogOption.swimlaneId);
this.selectedListId.set(this.cardDialogOption.listId);
}
}
subManager.subscribe('board', this.selectedBoardId.get(), false);
},
this.getBoardData(this.selectedBoardId.get());
if (!this.selectedSwimlaneId.get() || !Swimlanes.findOne({_id: this.selectedSwimlaneId.get(), boardId: this.selectedBoardId.get()})) {
this.setFirstSwimlaneId();
}
if (!this.selectedListId.get() || !Lists.findOne({_id: this.selectedListId.get(), boardId: this.selectedBoardId.get()})) {
this.setFirstListId();
}
}
/** sets the first swimlane id */
setFirstSwimlaneId() {
try {
const board = Boards.findOne(this.selectedBoardId.get());
const swimlaneId = board.swimlanes().fetch()[0]._id;
this.selectedSwimlaneId.set(swimlaneId);
} catch (e) {}
}
/** sets the first list id */
setFirstListId() {
try {
const board = Boards.findOne(this.selectedBoardId.get());
const listId = board.lists().fetch()[0]._id;
this.selectedListId.set(listId);
} catch (e) {}
}
/** returns if the board id was the last confirmed one
* @param boardId check this board id
* @return if the board id was the last confirmed one
*/
isMoveAndCopyDialogOptionBoardId(boardId) {
let ret = this.moveAndCopyDialogOption.boardId == boardId;
isCardDialogOptionBoardId(boardId) {
let ret = this.cardDialogOption.boardId == boardId;
return ret;
},
}
/** returns if the swimlane id was the last confirmed one
* @param swimlaneId check this swimlane id
* @return if the swimlane id was the last confirmed one
*/
isMoveAndCopyDialogOptionSwimlaneId(swimlaneId) {
let ret = this.moveAndCopyDialogOption.swimlaneId == swimlaneId;
isCardDialogOptionSwimlaneId(swimlaneId) {
let ret = this.cardDialogOption.swimlaneId == swimlaneId;
return ret;
},
}
/** returns if the list id was the last confirmed one
* @param listId check this list id
* @return if the list id was the last confirmed one
*/
isMoveAndCopyDialogOptionListId(listId) {
let ret = this.moveAndCopyDialogOption.listId == listId;
isCardDialogOptionListId(listId) {
let ret = this.cardDialogOption.listId == listId;
return ret;
},
}
/** returns all available board */
boards() {
return Boards.find(
const ret = Boards.find(
{
archived: false,
'members.userId': Meteor.userId(),
_id: { $ne: Meteor.user().getTemplatesBoardId() },
},
{
sort: { sort: 1 /* boards default sorting */ },
sort: { sort: 1 },
},
);
},
return ret;
}
/** returns all available swimlanes of the current board */
swimlanes() {
const board = Boards.findOne(this.selectedBoardId.get());
return board.swimlanes();
},
const ret = board.swimlanes();
return ret;
}
aBoardLists() {
/** returns all available lists of the current board */
lists() {
const board = Boards.findOne(this.selectedBoardId.get());
return board.lists();
},
const ret = board.lists();
return ret;
}
/** get the board data from the server
* @param boardId get the board data of this board id
*/
getBoardData(boardId) {
const self = this;
Meteor.subscribe('board', boardId, false, {
onReady() {
const sameBoardId = self.selectedBoardId.get() == boardId;
self.selectedBoardId.set(boardId);
if (!sameBoardId) {
// reset swimlane id (for selection in cards())
self.setFirstSwimlaneId();
// reset list id (for selection in cards())
self.setFirstListId();
}
},
});
}
events() {
return [
@ -945,52 +987,75 @@ BlazeComponent.extendComponent({
const swimlaneId = swimlaneSelect.options[swimlaneSelect.selectedIndex].value;
const options = {
'boardId': boardId,
'swimlaneId': swimlaneId,
'listId': listId,
'boardId' : boardId,
'swimlaneId' : swimlaneId,
'listId' : listId,
}
Meteor.user().setMoveAndCopyDialogOption(this.currentBoardId, options);
this.setDone(boardId, swimlaneId, listId, options);
Popup.back(2);
},
'change .js-select-boards'(event) {
const boardId = $(event.currentTarget).val();
this.selectedBoardId.set(boardId);
subManager.subscribe('board', boardId, false);
this.getBoardData(boardId);
},
'change .js-select-swimlanes'(event) {
this.selectedSwimlaneId.set($(event.currentTarget).val());
},
},
];
},
}).register('boardsAndLists');
}
}
Template.copyCardPopup.events({
'click .js-done'() {
const card = Utils.getCurrentCard();
const lSelect = $('.js-select-lists')[0];
const listId = lSelect.options[lSelect.selectedIndex].value;
const slSelect = $('.js-select-swimlanes')[0];
const swimlaneId = slSelect.options[slSelect.selectedIndex].value;
const bSelect = $('.js-select-boards')[0];
const boardId = bSelect.options[bSelect.selectedIndex].value;
const textarea = $('#copy-card-title');
/** Move Card Dialog */
(class extends DialogWithBoardSwimlaneList {
getCardDialogOptions() {
const ret = Meteor.user().getMoveAndCopyDialogOptions();
return ret;
}
setDone(boardId, swimlaneId, listId, options) {
Meteor.user().setMoveAndCopyDialogOption(this.currentBoardId, options);
const card = this.data();
const minOrder = card.getMinSort(listId, swimlaneId);
card.move(boardId, swimlaneId, listId, minOrder - 1);
}
}).register('moveCardPopup');
/** Copy Card Dialog */
(class extends DialogWithBoardSwimlaneList {
getCardDialogOptions() {
const ret = Meteor.user().getMoveAndCopyDialogOptions();
return ret;
}
setDone(boardId, swimlaneId, listId, options) {
Meteor.user().setMoveAndCopyDialogOption(this.currentBoardId, options);
const card = this.data();
// const textarea = $('#copy-card-title');
const textarea = this.$('#copy-card-title');
const title = textarea.val().trim();
// insert new card to the top of new list
const minOrder = card.getMinSort(listId, swimlaneId);
card.sort = minOrder - 1;
if (title) {
const oldTitle = card.title;
card.title = title;
card.coverId = '';
const _id = card.copy(boardId, swimlaneId, listId);
// insert new card to the top of new list
const minOrder = card.getMinSort(listId, swimlaneId);
card.sort = minOrder - 1;
const newCardId = card.copy(boardId, swimlaneId, listId);
// restore the old card title, otherwise the card title would change in the current view (only temporary)
card.title = oldTitle;
// In case the filter is active we need to add the newly inserted card in
// the list of exceptions -- cards that are not filtered. Otherwise the
// card will disappear instantly.
// See https://github.com/wekan/wekan/issues/80
Filter.addException(_id);
Popup.back(2);
Filter.addException(newCardId);
}
},
});
}
}).register('copyCardPopup');
Template.convertChecklistItemToCardPopup.events({
'click .js-done'() {
@ -1020,60 +1085,47 @@ Template.convertChecklistItemToCardPopup.events({
},
});
Template.copyChecklistToManyCardsPopup.events({
'click .js-done'() {
const card = Utils.getCurrentCard();
const oldId = card._id;
card._id = null;
const lSelect = $('.js-select-lists')[0];
card.listId = lSelect.options[lSelect.selectedIndex].value;
const slSelect = $('.js-select-swimlanes')[0];
card.swimlaneId = slSelect.options[slSelect.selectedIndex].value;
const bSelect = $('.js-select-boards')[0];
card.boardId = bSelect.options[bSelect.selectedIndex].value;
const textarea = $('#copy-card-title');
const titleEntry = textarea.val().trim();
// insert new card to the bottom of new list
card.sort = Lists.findOne(card.listId).cards().count();
/** Copy many cards dialog */
(class extends DialogWithBoardSwimlaneList {
getCardDialogOptions() {
const ret = Meteor.user().getMoveAndCopyDialogOptions();
return ret;
}
setDone(boardId, swimlaneId, listId, options) {
Meteor.user().setMoveAndCopyDialogOption(this.currentBoardId, options);
const card = this.data();
if (titleEntry) {
const titleList = JSON.parse(titleEntry);
const textarea = this.$('#copy-card-title');
const title = textarea.val().trim();
if (title) {
const oldTitle = card.title;
const titleList = JSON.parse(title);
for (let i = 0; i < titleList.length; i++) {
const obj = titleList[i];
card.title = obj.title;
card.description = obj.description;
card.coverId = '';
const _id = Cards.insert(card);
// insert new card to the top of new list
const maxOrder = card.getMaxSort(listId, swimlaneId);
card.sort = maxOrder + 1;
const newCardId = card.copy(boardId, swimlaneId, listId);
// In case the filter is active we need to add the newly inserted card in
// the list of exceptions -- cards that are not filtered. Otherwise the
// card will disappear instantly.
// See https://github.com/wekan/wekan/issues/80
Filter.addException(_id);
// copy checklists
Checklists.find({ cardId: oldId }).forEach((ch) => {
ch.copy(_id);
});
// copy subtasks
const cursor = Cards.find({ parentId: oldId });
cursor.forEach(function () {
'use strict';
const subtask = arguments[0];
subtask.parentId = _id;
subtask._id = null;
/* const newSubtaskId = */ Cards.insert(subtask);
});
// copy card comments
CardComments.find({ cardId: oldId }).forEach((cmt) => {
cmt.copy(_id);
});
Filter.addException(newCardId);
}
Popup.back();
// restore the old card title, otherwise the card title would change in the current view (only temporary)
card.title = oldTitle;
}
},
});
}
}).register('copyChecklistToManyCardsPopup');
BlazeComponent.extendComponent({
onCreated() {

View file

@ -609,6 +609,8 @@ Cards.helpers({
CardComments.find({ cardId: oldId }).forEach(cmt => {
cmt.copy(_id);
});
// restore the id, otherwise new copies will fail
this._id = oldId;
return _id;
},