summaryrefslogtreecommitdiffstats
path: root/client/components/cards/cardDetails.js
diff options
context:
space:
mode:
Diffstat (limited to 'client/components/cards/cardDetails.js')
-rw-r--r--client/components/cards/cardDetails.js221
1 files changed, 201 insertions, 20 deletions
diff --git a/client/components/cards/cardDetails.js b/client/components/cards/cardDetails.js
index 8d5c478d..26549fda 100644
--- a/client/components/cards/cardDetails.js
+++ b/client/components/cards/cardDetails.js
@@ -1,3 +1,6 @@
+const subManager = new SubsManager();
+const { calculateIndexData } = Utils;
+
BlazeComponent.extendComponent({
mixins() {
return [Mixins.InfiniteScrolling, Mixins.PerfectScrollbar];
@@ -18,9 +21,11 @@ BlazeComponent.extendComponent({
onCreated() {
this.isLoaded = new ReactiveVar(false);
- this.parentComponent().showOverlay.set(true);
- this.parentComponent().mouseHasEnterCardDetails = false;
+ this.parentComponent().parentComponent().showOverlay.set(true);
+ this.parentComponent().parentComponent().mouseHasEnterCardDetails = false;
this.calculateNextPeak();
+
+ Meteor.subscribe('unsaved-edits');
},
isWatching() {
@@ -28,16 +33,20 @@ BlazeComponent.extendComponent({
return card.findWatcher(Meteor.userId());
},
+ hiddenSystemMessages() {
+ return Meteor.user().hasHiddenSystemMessages();
+ },
+
canModifyCard() {
return Meteor.user() && Meteor.user().isBoardMember() && !Meteor.user().isCommentOnly();
},
scrollParentContainer() {
const cardPanelWidth = 510;
- const bodyBoardComponent = this.parentComponent();
+ const bodyBoardComponent = this.parentComponent().parentComponent();
- const $cardContainer = bodyBoardComponent.$('.js-lists');
const $cardView = this.$(this.firstNode());
+ const $cardContainer = bodyBoardComponent.$('.js-swimlanes');
const cardContainerScroll = $cardContainer.scrollLeft();
const cardContainerWidth = $cardContainer.width();
@@ -58,10 +67,55 @@ BlazeComponent.extendComponent({
onRendered() {
if (!Utils.isMiniScreen()) this.scrollParentContainer();
+ const $checklistsDom = this.$('.card-checklist-items');
+
+ $checklistsDom.sortable({
+ tolerance: 'pointer',
+ helper: 'clone',
+ handle: '.checklist-title',
+ items: '.js-checklist',
+ placeholder: 'checklist placeholder',
+ distance: 7,
+ start(evt, ui) {
+ ui.placeholder.height(ui.helper.height());
+ EscapeActions.executeUpTo('popup-close');
+ },
+ stop(evt, ui) {
+ let prevChecklist = ui.item.prev('.js-checklist').get(0);
+ if (prevChecklist) {
+ prevChecklist = Blaze.getData(prevChecklist).checklist;
+ }
+ let nextChecklist = ui.item.next('.js-checklist').get(0);
+ if (nextChecklist) {
+ nextChecklist = Blaze.getData(nextChecklist).checklist;
+ }
+ const sortIndex = calculateIndexData(prevChecklist, nextChecklist, 1);
+
+ $checklistsDom.sortable('cancel');
+ const checklist = Blaze.getData(ui.item.get(0)).checklist;
+
+ Checklists.update(checklist._id, {
+ $set: {
+ sort: sortIndex.base,
+ },
+ });
+ },
+ });
+
+ function userIsMember() {
+ return Meteor.user() && Meteor.user().isBoardMember();
+ }
+
+ // Disable sorting if the current user is not a board member
+ this.autorun(() => {
+ if ($checklistsDom.data('sortable')) {
+ $checklistsDom.sortable('option', 'disabled', !userIsMember());
+ }
+ });
},
onDestroyed() {
- this.parentComponent().showOverlay.set(false);
+ this.parentComponent().parentComponent().showOverlay.set(false);
},
events() {
@@ -95,9 +149,16 @@ BlazeComponent.extendComponent({
'click .js-member': Popup.open('cardMember'),
'click .js-add-members': Popup.open('cardMembers'),
'click .js-add-labels': Popup.open('cardLabels'),
+ 'click .js-received-date': Popup.open('editCardReceivedDate'),
+ 'click .js-start-date': Popup.open('editCardStartDate'),
+ 'click .js-due-date': Popup.open('editCardDueDate'),
+ 'click .js-end-date': Popup.open('editCardEndDate'),
'mouseenter .js-card-details' () {
- this.parentComponent().showOverlay.set(true);
- this.parentComponent().mouseHasEnterCardDetails = true;
+ this.parentComponent().parentComponent().showOverlay.set(true);
+ this.parentComponent().parentComponent().mouseHasEnterCardDetails = true;
+ },
+ 'click #toggleButton'() {
+ Meteor.call('toggleSystemMessages');
},
}];
},
@@ -154,20 +215,24 @@ Template.cardDetailsActionsPopup.events({
'click .js-members': Popup.open('cardMembers'),
'click .js-labels': Popup.open('cardLabels'),
'click .js-attachments': Popup.open('cardAttachments'),
+ 'click .js-received-date': Popup.open('editCardReceivedDate'),
'click .js-custom-fields': Popup.open('cardCustomFields'),
'click .js-start-date': Popup.open('editCardStartDate'),
'click .js-due-date': Popup.open('editCardDueDate'),
+ 'click .js-end-date': Popup.open('editCardEndDate'),
+ 'click .js-spent-time': Popup.open('editCardSpentTime'),
'click .js-move-card': Popup.open('moveCard'),
'click .js-copy-card': Popup.open('copyCard'),
+ 'click .js-copy-checklist-cards': Popup.open('copyChecklistToManyCards'),
'click .js-move-card-to-top' (evt) {
evt.preventDefault();
- const minOrder = _.min(this.list().cards().map((c) => c.sort));
- this.move(this.listId, minOrder - 1);
+ const minOrder = _.min(this.list().cards(this.swimlaneId).map((c) => c.sort));
+ this.move(this.swimlaneId, this.listId, minOrder - 1);
},
'click .js-move-card-to-bottom' (evt) {
evt.preventDefault();
- const maxOrder = _.max(this.list().cards().map((c) => c.sort));
- this.move(this.listId, maxOrder + 1);
+ const maxOrder = _.max(this.list().cards(this.swimlaneId).map((c) => c.sort));
+ this.move(this.swimlaneId, this.listId, maxOrder + 1);
},
'click .js-archive' (evt) {
evt.preventDefault();
@@ -191,36 +256,82 @@ Template.editCardTitleForm.onRendered(function () {
Template.editCardTitleForm.events({
'keydown .js-edit-card-title' (evt) {
// If enter key was pressed, submit the data
- if (evt.keyCode === 13) {
+ // Unless the shift key is also being pressed
+ if (evt.keyCode === 13 && !evt.shiftKey) {
$('.js-submit-edit-card-title-form').click();
}
},
});
Template.moveCardPopup.events({
- 'click .js-select-list' () {
+ 'click .js-done' () {
// XXX We should *not* get the currentCard from the global state, but
// instead from a “component” state.
const card = Cards.findOne(Session.get('currentCard'));
- const newListId = this._id;
- card.move(newListId);
+ const lSelect = $('.js-select-lists')[0];
+ const newListId = lSelect.options[lSelect.selectedIndex].value;
+ const slSelect = $('.js-select-swimlanes')[0];
+ card.swimlaneId = slSelect.options[slSelect.selectedIndex].value;
+ card.move(card.swimlaneId, newListId, 0);
Popup.close();
},
});
+BlazeComponent.extendComponent({
+ onCreated() {
+ subManager.subscribe('board', Session.get('currentBoard'));
+ this.selectedBoardId = new ReactiveVar(Session.get('currentBoard'));
+ },
+
+ boards() {
+ const boards = Boards.find({
+ archived: false,
+ 'members.userId': Meteor.userId(),
+ }, {
+ sort: ['title'],
+ });
+ return boards;
+ },
+
+ swimlanes() {
+ const board = Boards.findOne(this.selectedBoardId.get());
+ return board.swimlanes();
+ },
+
+ aBoardLists() {
+ const board = Boards.findOne(this.selectedBoardId.get());
+ return board.lists();
+ },
+
+ events() {
+ return [{
+ 'change .js-select-boards'(evt) {
+ this.selectedBoardId.set($(evt.currentTarget).val());
+ subManager.subscribe('board', this.selectedBoardId.get());
+ },
+ }];
+ },
+}).register('boardsAndLists');
+
Template.copyCardPopup.events({
- 'click .js-select-list' (evt) {
+ 'click .js-done'() {
const card = Cards.findOne(Session.get('currentCard'));
const oldId = card._id;
card._id = null;
- card.listId = this._id;
- const textarea = $(evt.currentTarget).parents('.content').find('textarea');
+ 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 title = textarea.val().trim();
// insert new card to the bottom of new list
- card.sort = Lists.findOne(this._id).cards().count();
+ card.sort = Lists.findOne(card.listId).cards().count();
if (title) {
card.title = title;
+ card.coverId = '';
const _id = Cards.insert(card);
// 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
@@ -233,9 +344,16 @@ Template.copyCardPopup.events({
cursor.forEach(function() {
'use strict';
const checklist = arguments[0];
+ const checklistId = checklist._id;
checklist.cardId = _id;
checklist._id = null;
- Checklists.insert(checklist);
+ const newChecklistId = Checklists.insert(checklist);
+ ChecklistItems.find({checklistId}).forEach(function(item) {
+ item._id = null;
+ item.checklistId = newChecklistId;
+ item.cardId = _id;
+ ChecklistItems.insert(item);
+ });
});
// copy card comments
@@ -252,6 +370,69 @@ Template.copyCardPopup.events({
},
});
+Template.copyChecklistToManyCardsPopup.events({
+ 'click .js-done' () {
+ const card = Cards.findOne(Session.get('currentCard'));
+ 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();
+
+ if (titleEntry) {
+ const titleList = JSON.parse(titleEntry);
+ 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);
+ // 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
+ let cursor = Checklists.find({cardId: oldId});
+ cursor.forEach(function() {
+ 'use strict';
+ const checklist = arguments[0];
+ const checklistId = checklist._id;
+ checklist.cardId = _id;
+ checklist._id = null;
+ const newChecklistId = Checklists.insert(checklist);
+ ChecklistItems.find({checklistId}).forEach(function(item) {
+ item._id = null;
+ item.checklistId = newChecklistId;
+ item.cardId = _id;
+ ChecklistItems.insert(item);
+ });
+ });
+
+ // copy card comments
+ cursor = CardComments.find({cardId: oldId});
+ cursor.forEach(function () {
+ 'use strict';
+ const comment = arguments[0];
+ comment.cardId = _id;
+ comment._id = null;
+ CardComments.insert(comment);
+ });
+ }
+ Popup.close();
+ }
+ },
+});
+
+
Template.cardMorePopup.events({
'click .js-copy-card-link-to-clipboard' () {
// Clipboard code from: