Преглед на файлове

Merge pull request #4647 from mfilser/copy_and_move_card_checklists_same_code

Copy / move card and checklists using same code
Lauri Ojansivu преди 2 години
родител
ревизия
a9334b3bf4

+ 9 - 3
client/components/cards/cardDetails.jade

@@ -725,6 +725,12 @@ template(name="copyManyCardsPopup")
     | {{_ 'copyManyCardsPopup-format'}}
   +copyAndMoveCard
 
+template(name="convertChecklistItemToCardPopup")
+  label(for='convert-checklist-item-to-card-title') {{_ 'title'}}:
+  textarea#copy-card-title.minicard-composer-textarea.js-card-title(autofocus)
+    = item.title
+  +copyAndMoveCard
+
 template(name="copyAndMoveCard")
   unless currentUser.isWorker
     label {{_ 'boards'}}:
@@ -733,17 +739,17 @@ template(name="copyAndMoveCard")
         if $eq _id currentBoard._id
           option(value="{{_id}}" selected) {{_ 'current'}}
         else
-          option(value="{{_id}}" selected="{{#if isCardDialogOptionBoardId _id}}selected{{/if}}") {{title}}
+          option(value="{{_id}}" selected="{{#if isDialogOptionBoardId _id}}selected{{/if}}") {{title}}
 
   label {{_ 'swimlanes'}}:
   select.js-select-swimlanes
     each swimlanes
-      option(value="{{_id}}" selected="{{#if isCardDialogOptionSwimlaneId _id}}selected{{/if}}") {{title}}
+      option(value="{{_id}}" selected="{{#if isDialogOptionSwimlaneId _id}}selected{{/if}}") {{title}}
 
   label {{_ 'lists'}}:
   select.js-select-lists
     each lists
-      option(value="{{_id}}" selected="{{#if isCardDialogOptionListId _id}}selected{{/if}}") {{title}}
+      option(value="{{_id}}" selected="{{#if isDialogOptionListId _id}}selected{{/if}}") {{title}}
 
   .edit-controls.clearfix
     button.primary.confirm.js-done {{_ 'done'}}

+ 21 - 198
client/components/cards/cardDetails.js

@@ -10,6 +10,7 @@ import Lists from '/models/lists';
 import CardComments from '/models/cardComments';
 import { ALLOWED_COLORS } from '/config/const';
 import { UserAvatar } from '../users/userAvatar';
+import { DialogWithBoardSwimlaneList } from '/client/lib/dialogWithBoardSwimlaneList';
 
 const subManager = new SubsManager();
 const { calculateIndexData } = Utils;
@@ -826,189 +827,9 @@ Template.editCardAssignerForm.events({
   },
 });
 
-class DialogWithBoardSwimlaneList extends BlazeComponent {
-  /** returns the card dialog options
-   * @return Object with properties { boardId, swimlaneId, listId }
-   */
-  getCardDialogOptions() {
-  }
-
-  /** list is done
-   * @param listId the selected list id
-   * @param options the selected options (Object with properties { boardId, swimlaneId, listId })
-   */
-  setDone(listId, options) {
-  }
-
-  onCreated() {
-    this.currentBoardId = Utils.getCurrentBoardId();
-    this.selectedBoardId = new ReactiveVar(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
-   */
-  setCardDialogOption(boardId) {
-    this.cardDialogOption = {
-      'boardId' : "",
-      'swimlaneId' : "",
-      'listId' : "",
-    }
-
-    let currentOptions = this.getCardDialogOptions();
-    if (currentOptions && boardId && currentOptions[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);
-      }
-    }
-    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
-   */
-  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
-   */
-  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
-   */
-  isCardDialogOptionListId(listId) {
-    let ret = this.cardDialogOption.listId == listId;
-    return ret;
-  }
-
-  /** returns all available board */
-  boards() {
-    const ret = Boards.find(
-      {
-        archived: false,
-        'members.userId': Meteor.userId(),
-        _id: { $ne: Meteor.user().getTemplatesBoardId() },
-      },
-      {
-        sort: { sort: 1 },
-      },
-    );
-    return ret;
-  }
-
-  /** returns all available swimlanes of the current board */
-  swimlanes() {
-    const board = Boards.findOne(this.selectedBoardId.get());
-    const ret = board.swimlanes();
-    return ret;
-  }
-
-  /** returns all available lists of the current board */
-  lists() {
-    const board = Boards.findOne(this.selectedBoardId.get());
-    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 [
-      {
-        'click .js-done'() {
-          const boardSelect = this.$('.js-select-boards')[0];
-          const boardId = boardSelect.options[boardSelect.selectedIndex].value;
-
-          const listSelect = this.$('.js-select-lists')[0];
-          const listId = listSelect.options[listSelect.selectedIndex].value;
-
-          const swimlaneSelect = this.$('.js-select-swimlanes')[0];
-          const swimlaneId = swimlaneSelect.options[swimlaneSelect.selectedIndex].value;
-
-          const options = {
-            'boardId' : boardId,
-            'swimlaneId' : swimlaneId,
-            'listId' : listId,
-          }
-          this.setDone(boardId, swimlaneId, listId, options);
-          Popup.back(2);
-        },
-        'change .js-select-boards'(event) {
-          const boardId = $(event.currentTarget).val();
-          this.getBoardData(boardId);
-        },
-        'change .js-select-swimlanes'(event) {
-          this.selectedSwimlaneId.set($(event.currentTarget).val());
-        },
-      },
-    ];
-  }
-}
-
 /** Move Card Dialog */
 (class extends DialogWithBoardSwimlaneList {
-  getCardDialogOptions() {
+  getDialogOptions() {
     const ret = Meteor.user().getMoveAndCopyDialogOptions();
     return ret;
   }
@@ -1022,7 +843,7 @@ class DialogWithBoardSwimlaneList extends BlazeComponent {
 
 /** Copy Card Dialog */
 (class extends DialogWithBoardSwimlaneList {
-  getCardDialogOptions() {
+  getDialogOptions() {
     const ret = Meteor.user().getMoveAndCopyDialogOptions();
     return ret;
   }
@@ -1047,16 +868,17 @@ class DialogWithBoardSwimlaneList extends BlazeComponent {
   }
 }).register('copyCardPopup');
 
-Template.convertChecklistItemToCardPopup.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');
+/** Convert Checklist-Item to card dialog */
+(class extends DialogWithBoardSwimlaneList {
+  getDialogOptions() {
+    const ret = Meteor.user().getMoveAndCopyDialogOptions();
+    return ret;
+  }
+  setDone(boardId, swimlaneId, listId, options) {
+    Meteor.user().setMoveAndCopyDialogOption(this.currentBoardId, options);
+    const card = this.data();
+
+    const textarea = this.$('#copy-card-title');
     const title = textarea.val().trim();
 
     if (title) {
@@ -1067,17 +889,18 @@ Template.convertChecklistItemToCardPopup.events({
         swimlaneId: swimlaneId,
         sort: 0,
       });
-      Filter.addException(_id);
-
-      Popup.back();
+      const card = Cards.findOne(_id);
+      const minOrder = card.getMinSort();
+      card.move(card.boardId, card.swimlaneId, card.listId, minOrder - 1);
 
+      Filter.addException(_id);
     }
-  },
-});
+  }
+}).register('convertChecklistItemToCardPopup');
 
 /** Copy many cards dialog */
 (class extends DialogWithBoardSwimlaneList {
-  getCardDialogOptions() {
+  getDialogOptions() {
     const ret = Meteor.user().getMoveAndCopyDialogOptions();
     return ret;
   }

+ 4 - 33
client/components/cards/checklists.jade

@@ -115,35 +115,6 @@ template(name='checklistItemDetail')
         +viewer
           = item.title
 
-template(name="convertChecklistItemToCardPopup")
-  label(for='convert-checklist-item-to-card-title') {{_ 'title'}}:
-  textarea#copy-card-title.minicard-composer-textarea.js-card-title(autofocus)
-    = item.title
-  +boardsSwimlanesAndLists
-
-template(name="boardsSwimlanesAndLists")
-  unless currentUser.isWorker
-    label {{_ 'boards'}}:
-    select.js-select-boards(autofocus)
-      each boards
-        if $eq _id currentBoard._id
-          option(value="{{_id}}" selected) {{_ 'current'}}
-        else
-          option(value="{{_id}}") {{title}}
-
-  label {{_ 'swimlanes'}}:
-  select.js-select-swimlanes
-    each swimlanes
-      option(value="{{_id}}") {{title}}
-
-  label {{_ 'lists'}}:
-  select.js-select-lists
-    each aBoardLists
-      option(value="{{_id}}") {{title}}
-
-  .edit-controls.clearfix
-    button.primary.confirm.js-done {{_ 'done'}}
-
 template(name="checklistActionsPopup")
   ul.pop-over-list
     li
@@ -171,22 +142,22 @@ template(name="copyAndMoveChecklist")
         if $eq _id currentBoard._id
           option(value="{{_id}}" selected) {{_ 'current'}}
         else
-          option(value="{{_id}}" selected="{{#if isChecklistDialogOptionBoardId _id}}selected{{/if}}") {{title}}
+          option(value="{{_id}}" selected="{{#if isDialogOptionBoardId _id}}selected{{/if}}") {{title}}
 
   label {{_ 'swimlanes'}}:
   select.js-select-swimlanes
     each swimlanes
-      option(value="{{_id}}" selected="{{#if isChecklistDialogOptionSwimlaneId _id}}selected{{/if}}") {{title}}
+      option(value="{{_id}}" selected="{{#if isDialogOptionSwimlaneId _id}}selected{{/if}}") {{title}}
 
   label {{_ 'lists'}}:
   select.js-select-lists
     each lists
-      option(value="{{_id}}" selected="{{#if isChecklistDialogOptionListId _id}}selected{{/if}}") {{title}}
+      option(value="{{_id}}" selected="{{#if isDialogOptionListId _id}}selected{{/if}}") {{title}}
 
   label {{_ 'cards'}}:
   select.js-select-cards
     each cards
-      option(value="{{_id}}" selected="{{#if isChecklistDialogOptionCardId _id}}selected{{/if}}") {{title}}
+      option(value="{{_id}}" selected="{{#if isDialogOptionCardId _id}}selected{{/if}}") {{title}}
 
   .edit-controls.clearfix
     button.primary.confirm.js-done {{_ 'done'}}

+ 5 - 207
client/components/cards/checklists.js

@@ -1,6 +1,7 @@
 import { TAPi18n } from '/imports/i18n';
 import Cards from '/models/cards';
 import Boards from '/models/boards';
+import { DialogWithBoardSwimlaneListCard } from '/client/lib/dialogWithBoardSwimlaneListCard';
 
 const subManager = new SubsManager();
 const { calculateIndexData, capitalize } = Utils;
@@ -380,212 +381,9 @@ BlazeComponent.extendComponent({
   },
 }).register('checklistItemDetail');
 
-class DialogWithBoardSwimlaneListAndCard extends BlazeComponent {
-  /** returns the checklist dialog options
-   * @return Object with properties { boardId, swimlaneId, listId, cardId }
-   */
-  getChecklistDialogOptions() {
-  }
-
-  /** checklist is done
-   * @param cardId the selected card id
-   * @param options the selected options (Object with properties { boardId, swimlaneId, listId, cardId })
-   */
-  setDone(cardId, options) {
-  }
-
-  onCreated() {
-    this.currentBoardId = Utils.getCurrentBoardId();
-    this.selectedBoardId = new ReactiveVar(this.currentBoardId);
-    this.selectedSwimlaneId = new ReactiveVar('');
-    this.selectedListId = new ReactiveVar('');
-    this.setChecklistDialogOption(this.currentBoardId);
-  }
-
-  /** set the last confirmed dialog field values
-   * @param boardId the current board id
-   */
-  setChecklistDialogOption(boardId) {
-    this.checklistDialogOption = {
-      'boardId': "",
-      'swimlaneId': "",
-      'listId': "",
-      'cardId': "",
-    }
-
-    let currentOptions = this.getChecklistDialogOptions();
-    if (currentOptions && boardId && currentOptions[boardId]) {
-      this.checklistDialogOption = currentOptions[boardId];
-      if (this.checklistDialogOption.boardId &&
-        this.checklistDialogOption.swimlaneId &&
-        this.checklistDialogOption.listId
-      ) {
-        this.selectedBoardId.set(this.checklistDialogOption.boardId)
-        this.selectedSwimlaneId.set(this.checklistDialogOption.swimlaneId);
-        this.selectedListId.set(this.checklistDialogOption.listId);
-      }
-    }
-    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
-   */
-  isChecklistDialogOptionBoardId(boardId) {
-    let ret = this.checklistDialogOption.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
-   */
-  isChecklistDialogOptionSwimlaneId(swimlaneId) {
-    let ret = this.checklistDialogOption.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
-   */
-  isChecklistDialogOptionListId(listId) {
-    let ret = this.checklistDialogOption.listId == listId;
-    return ret;
-  }
-
-  /** returns if the card id was the last confirmed one
-   * @param cardId check this card id
-   * @return if the card id was the last confirmed one
-   */
-  isChecklistDialogOptionCardId(cardId) {
-    let ret = this.checklistDialogOption.cardId == cardId;
-    return ret;
-  }
-
-  /** returns all available board */
-  boards() {
-    const ret = Boards.find(
-      {
-        archived: false,
-        'members.userId': Meteor.userId(),
-        _id: { $ne: Meteor.user().getTemplatesBoardId() },
-      },
-      {
-        sort: { sort: 1 },
-      },
-    );
-    return ret;
-  }
-
-  /** returns all available swimlanes of the current board */
-  swimlanes() {
-    const board = Boards.findOne(this.selectedBoardId.get());
-    const ret = board.swimlanes();
-    return ret;
-  }
-
-  /** returns all available lists of the current board */
-  lists() {
-    const board = Boards.findOne(this.selectedBoardId.get());
-    const ret = board.lists();
-    return ret;
-  }
-
-  /** returns all available cards of the current list */
-  cards() {
-    const list = Lists.findOne(this.selectedListId.get());
-    const ret = list.cards(this.selectedSwimlaneId.get());
-    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 [
-      {
-        'click .js-done'() {
-          const boardSelect = this.$('.js-select-boards')[0];
-          const boardId = boardSelect.options[boardSelect.selectedIndex].value;
-
-          const listSelect = this.$('.js-select-lists')[0];
-          const listId = listSelect.options[listSelect.selectedIndex].value;
-
-          const swimlaneSelect = this.$('.js-select-swimlanes')[0];
-          const swimlaneId = swimlaneSelect.options[swimlaneSelect.selectedIndex].value;
-
-          const cardSelect = this.$('.js-select-cards')[0];
-          const cardId = cardSelect.options[cardSelect.selectedIndex].value;
-
-          const options = {
-            'boardId': boardId,
-            'swimlaneId': swimlaneId,
-            'listId': listId,
-            'cardId': cardId,
-          }
-          this.setDone(cardId, options);
-          Popup.back(2);
-        },
-        'change .js-select-boards'(event) {
-          const boardId = $(event.currentTarget).val();
-          this.getBoardData(boardId);
-        },
-        'change .js-select-swimlanes'(event) {
-          this.selectedSwimlaneId.set($(event.currentTarget).val());
-        },
-        'change .js-select-lists'(event) {
-          this.selectedListId.set($(event.currentTarget).val());
-        },
-      },
-    ];
-  }
-}
-
 /** Move Checklist Dialog */
-(class extends DialogWithBoardSwimlaneListAndCard {
-  getChecklistDialogOptions() {
+(class extends DialogWithBoardSwimlaneListCard {
+  getDialogOptions() {
     const ret = Meteor.user().getMoveChecklistDialogOptions();
     return ret;
   }
@@ -596,8 +394,8 @@ class DialogWithBoardSwimlaneListAndCard extends BlazeComponent {
 }).register('moveChecklistPopup');
 
 /** Copy Checklist Dialog */
-(class extends DialogWithBoardSwimlaneListAndCard {
-  getChecklistDialogOptions() {
+(class extends DialogWithBoardSwimlaneListCard {
+  getDialogOptions() {
     const ret = Meteor.user().getCopyChecklistDialogOptions();
     return ret;
   }

+ 188 - 0
client/lib/dialogWithBoardSwimlaneList.js

@@ -0,0 +1,188 @@
+export class DialogWithBoardSwimlaneList extends BlazeComponent {
+  /** returns the card dialog options
+   * @return Object with properties { boardId, swimlaneId, listId }
+   */
+  getDialogOptions() {
+  }
+
+  /** list is done
+   * @param listId the selected list id
+   * @param options the selected options (Object with properties { boardId, swimlaneId, listId })
+   */
+  setDone(listId, options) {
+  }
+
+  /** get the default options
+   * @return the options
+   */
+  getDefaultOption(boardId) {
+    const ret = {
+      'boardId' : "",
+      'swimlaneId' : "",
+      'listId' : "",
+    }
+    return ret;
+  }
+
+  onCreated() {
+    this.currentBoardId = Utils.getCurrentBoardId();
+    this.selectedBoardId = new ReactiveVar(this.currentBoardId);
+    this.selectedSwimlaneId = new ReactiveVar('');
+    this.selectedListId = new ReactiveVar('');
+    this.setOption(this.currentBoardId);
+  }
+
+  /** set the last confirmed dialog field values
+   * @param boardId the current board id
+   */
+  setOption(boardId) {
+    this.cardOption = this.getDefaultOption();
+
+    let currentOptions = this.getDialogOptions();
+    if (currentOptions && boardId && currentOptions[boardId]) {
+      this.cardOption = currentOptions[boardId];
+      if (this.cardOption.boardId &&
+          this.cardOption.swimlaneId &&
+          this.cardOption.listId
+      )
+      {
+        this.selectedBoardId.set(this.cardOption.boardId)
+        this.selectedSwimlaneId.set(this.cardOption.swimlaneId);
+        this.selectedListId.set(this.cardOption.listId);
+      }
+    }
+    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
+   */
+  isDialogOptionBoardId(boardId) {
+    let ret = this.cardOption.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
+   */
+  isDialogOptionSwimlaneId(swimlaneId) {
+    let ret = this.cardOption.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
+   */
+  isDialogOptionListId(listId) {
+    let ret = this.cardOption.listId == listId;
+    return ret;
+  }
+
+  /** returns all available board */
+  boards() {
+    const ret = Boards.find(
+      {
+        archived: false,
+        'members.userId': Meteor.userId(),
+        _id: { $ne: Meteor.user().getTemplatesBoardId() },
+      },
+      {
+        sort: { sort: 1 },
+      },
+    );
+    return ret;
+  }
+
+  /** returns all available swimlanes of the current board */
+  swimlanes() {
+    const board = Boards.findOne(this.selectedBoardId.get());
+    const ret = board.swimlanes();
+    return ret;
+  }
+
+  /** returns all available lists of the current board */
+  lists() {
+    const board = Boards.findOne(this.selectedBoardId.get());
+    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 [
+      {
+        'click .js-done'() {
+          const boardSelect = this.$('.js-select-boards')[0];
+          const boardId = boardSelect.options[boardSelect.selectedIndex].value;
+
+          const listSelect = this.$('.js-select-lists')[0];
+          const listId = listSelect.options[listSelect.selectedIndex].value;
+
+          const swimlaneSelect = this.$('.js-select-swimlanes')[0];
+          const swimlaneId = swimlaneSelect.options[swimlaneSelect.selectedIndex].value;
+
+          const options = {
+            'boardId' : boardId,
+            'swimlaneId' : swimlaneId,
+            'listId' : listId,
+          }
+          this.setDone(boardId, swimlaneId, listId, options);
+          Popup.back(2);
+        },
+        'change .js-select-boards'(event) {
+          const boardId = $(event.currentTarget).val();
+          this.getBoardData(boardId);
+        },
+        'change .js-select-swimlanes'(event) {
+          this.selectedSwimlaneId.set($(event.currentTarget).val());
+        },
+      },
+    ];
+  }
+}
+

+ 71 - 0
client/lib/dialogWithBoardSwimlaneListCard.js

@@ -0,0 +1,71 @@
+import { DialogWithBoardSwimlaneList } from '/client/lib/dialogWithBoardSwimlaneList';
+
+export class DialogWithBoardSwimlaneListCard extends DialogWithBoardSwimlaneList {
+  getDefaultOption(boardId) {
+    const ret = {
+      'boardId' : "",
+      'swimlaneId' : "",
+      'listId' : "",
+      'cardId': "",
+    }
+    return ret;
+  }
+
+  /** returns if the card id was the last confirmed one
+   * @param cardId check this card id
+   * @return if the card id was the last confirmed one
+   */
+  isDialogOptionCardId(cardId) {
+    let ret = this.cardOption.cardId == cardId;
+    return ret;
+  }
+
+  /** returns all available cards of the current list */
+  cards() {
+    const list = Lists.findOne(this.selectedListId.get());
+    let ret = {}
+    if (list) {
+      ret = list.cards(this.selectedSwimlaneId.get());
+    }
+    return ret;
+  }
+
+  events() {
+    return [
+      {
+        'click .js-done'() {
+          const boardSelect = this.$('.js-select-boards')[0];
+          const boardId = boardSelect.options[boardSelect.selectedIndex].value;
+
+          const listSelect = this.$('.js-select-lists')[0];
+          const listId = listSelect.options[listSelect.selectedIndex].value;
+
+          const swimlaneSelect = this.$('.js-select-swimlanes')[0];
+          const swimlaneId = swimlaneSelect.options[swimlaneSelect.selectedIndex].value;
+
+          const cardSelect = this.$('.js-select-cards')[0];
+          const cardId = cardSelect.options[cardSelect.selectedIndex].value;
+
+          const options = {
+            'boardId' : boardId,
+            'swimlaneId' : swimlaneId,
+            'listId' : listId,
+            'cardId': cardId,
+          }
+          this.setDone(cardId, options);
+          Popup.back(2);
+        },
+        'change .js-select-boards'(event) {
+          const boardId = $(event.currentTarget).val();
+          this.getBoardData(boardId);
+        },
+        'change .js-select-swimlanes'(event) {
+          this.selectedSwimlaneId.set($(event.currentTarget).val());
+        },
+        'change .js-select-lists'(event) {
+          this.selectedListId.set($(event.currentTarget).val());
+        },
+      },
+    ];
+  }
+}