summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorAndrés Manelli <andresmanelli@gmail.com>2018-03-20 00:13:42 -0300
committerAndrés Manelli <andresmanelli@gmail.com>2018-08-10 23:55:19 +0200
commitdcc7b2970f3635b95bc71e3fc163a51cacad0931 (patch)
tree58f4bffdfef29b2bdab8d04a64fd8c873a0f20a4
parent193af893ee4da894e9494792306f5825e99de74a (diff)
downloadwekan-dcc7b2970f3635b95bc71e3fc163a51cacad0931.tar.gz
wekan-dcc7b2970f3635b95bc71e3fc163a51cacad0931.tar.bz2
wekan-dcc7b2970f3635b95bc71e3fc163a51cacad0931.zip
Add UI for importing card-as-card and board-as-card
-rw-r--r--client/components/forms/forms.styl3
-rw-r--r--client/components/lists/list.styl11
-rw-r--r--client/components/lists/listBody.jade53
-rw-r--r--client/components/lists/listBody.js106
-rw-r--r--i18n/en.i18n.json5
-rw-r--r--models/boards.js4
-rw-r--r--models/cards.js7
-rw-r--r--server/migrations.js11
8 files changed, 199 insertions, 1 deletions
diff --git a/client/components/forms/forms.styl b/client/components/forms/forms.styl
index 0a905943..5be70b7a 100644
--- a/client/components/forms/forms.styl
+++ b/client/components/forms/forms.styl
@@ -225,9 +225,12 @@ textarea
.edit-controls,
.add-controls
+ display: flex
+ align-items: baseline
margin-top: 0
button[type=submit]
+ input[type=button]
float: left
height: 32px
margin-top: -2px
diff --git a/client/components/lists/list.styl b/client/components/lists/list.styl
index fa32ff6d..0e170ebf 100644
--- a/client/components/lists/list.styl
+++ b/client/components/lists/list.styl
@@ -187,3 +187,14 @@
padding: 7px
top: -@padding
right: 17px
+
+.import-board-wrapper
+ display: flex
+ align-items: baseline
+
+ .js-import-board
+ margin-left: 15px
+
+.search-card-results
+ max-height: 250px
+ overflow: hidden
diff --git a/client/components/lists/listBody.jade b/client/components/lists/listBody.jade
index 32c6b278..e0655fc2 100644
--- a/client/components/lists/listBody.jade
+++ b/client/components/lists/listBody.jade
@@ -34,8 +34,59 @@ template(name="addCardForm")
.add-controls.clearfix
button.primary.confirm(type="submit") {{_ 'add'}}
- a.fa.fa-times-thin.js-close-inlined-form
+ span.quiet
+ | {{_ 'or'}}
+ a.js-import {{_ 'import'}}
template(name="autocompleteLabelLine")
.minicard-label(class="card-label-{{colorName}}" title=labelName)
span(class="{{#if hasNoName}}quiet{{/if}}")= labelName
+
+template(name="importCardPopup")
+ label {{_ 'boards'}}:
+ .import-board-wrapper
+ select.js-select-boards
+ each boards
+ if $eq _id currentBoard._id
+ option(value="{{_id}}" selected) {{_ 'current'}}
+ else
+ option(value="{{_id}}") {{title}}
+ input.primary.confirm.js-import-board(type="submit" value="{{_ 'add'}}")
+
+ label {{_ 'swimlanes'}}:
+ select.js-select-swimlanes
+ each swimlanes
+ option(value="{{_id}}") {{title}}
+
+ label {{_ 'lists'}}:
+ select.js-select-lists
+ each lists
+ option(value="{{_id}}") {{title}}
+
+ label {{_ 'cards'}}:
+ select.js-select-lists
+ each cards
+ option(value="{{_id}}") {{title}}
+
+ .edit-controls.clearfix
+ input.primary.confirm.js-done(type="submit" value="{{_ 'done'}}")
+ span.quiet
+ | {{_ 'or'}}
+ a.js-search {{_ 'search'}}
+
+template(name="searchCardPopup")
+ label {{_ 'boards'}}:
+ .import-board-wrapper
+ select.js-select-boards
+ each boards
+ if $eq _id currentBoard._id
+ option(value="{{_id}}" selected) {{_ 'current'}}
+ else
+ option(value="{{_id}}") {{title}}
+ form.js-search-term-form
+ input(type="text" name="searchTerm" placeholder="{{_ 'search-example'}}" autofocus)
+ .list-body.js-perfect-scrollbar.search-card-results
+ .minicards.clearfix.js-minicards
+ each results
+ a.minicard-wrapper.js-minicard
+ +minicard(this)
diff --git a/client/components/lists/listBody.js b/client/components/lists/listBody.js
index 0a10f7d5..4cae9f0b 100644
--- a/client/components/lists/listBody.js
+++ b/client/components/lists/listBody.js
@@ -1,3 +1,5 @@
+const subManager = new SubsManager();
+
BlazeComponent.extendComponent({
mixins() {
return [Mixins.PerfectScrollbar];
@@ -55,6 +57,7 @@ BlazeComponent.extendComponent({
boardId: boardId._id,
sort: sortIndex,
swimlaneId,
+ type: 'cardType-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
@@ -197,6 +200,7 @@ BlazeComponent.extendComponent({
events() {
return [{
keydown: this.pressKey,
+ 'click .js-import': Popup.open('importCard'),
}];
},
@@ -268,3 +272,105 @@ BlazeComponent.extendComponent({
});
},
}).register('addCardForm');
+
+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();
+ },
+
+ lists() {
+ const board = Boards.findOne(this.selectedBoardId.get());
+ return board.lists();
+ },
+
+ cards() {
+ const board = Boards.findOne(this.selectedBoardId.get());
+ return board.cards();
+ },
+
+ events() {
+ return [{
+ 'change .js-select-boards'(evt) {
+ this.selectedBoardId.set($(evt.currentTarget).val());
+ subManager.subscribe('board', this.selectedBoardId.get());
+ },
+ 'submit .js-done' (evt) {
+ // IMPORT CARD
+ evt.preventDefault();
+ // 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 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;
+ Popup.close();
+ },
+ 'submit .js-import-board' (evt) {
+ //IMPORT BOARD
+ evt.preventDefault();
+ Popup.close();
+ },
+ 'click .js-search': Popup.open('searchCard'),
+ }];
+ },
+}).register('importCardPopup');
+
+BlazeComponent.extendComponent({
+ mixins() {
+ return [Mixins.PerfectScrollbar];
+ },
+
+ onCreated() {
+ subManager.subscribe('board', Session.get('currentBoard'));
+ this.selectedBoardId = new ReactiveVar(Session.get('currentBoard'));
+ this.term = new ReactiveVar('');
+ },
+
+ boards() {
+ const boards = Boards.find({
+ archived: false,
+ 'members.userId': Meteor.userId(),
+ }, {
+ sort: ['title'],
+ });
+ return boards;
+ },
+
+ results() {
+ const board = Boards.findOne(this.selectedBoardId.get());
+ return board.searchCards(this.term.get());
+ },
+
+ events() {
+ return [{
+ 'change .js-select-boards'(evt) {
+ this.selectedBoardId.set($(evt.currentTarget).val());
+ subManager.subscribe('board', this.selectedBoardId.get());
+ },
+ 'submit .js-search-term-form'(evt) {
+ evt.preventDefault();
+ this.term.set(evt.target.searchTerm.value);
+ },
+ 'click .js-minicard'() {
+ // IMPORT CARD
+ },
+ }];
+ },
+}).register('searchCardPopup');
diff --git a/i18n/en.i18n.json b/i18n/en.i18n.json
index 9244af9c..08fc129f 100644
--- a/i18n/en.i18n.json
+++ b/i18n/en.i18n.json
@@ -135,6 +135,9 @@
"cards": "Cards",
"cards-count": "Cards",
"casSignIn" : "Sign In with CAS",
+ "cardType-card": "Card",
+ "cardType-importedCard": "Imported Card",
+ "cardType-importedBoard": "Imported Board",
"change": "Change",
"change-avatar": "Change Avatar",
"change-password": "Change Password",
@@ -171,6 +174,8 @@
"confirm-subtask-delete-dialog": "Are you sure you want to delete subtask?",
"confirm-checklist-delete-dialog": "Are you sure you want to delete checklist?",
"copy-card-link-to-clipboard": "Copy card link to clipboard",
+ "importCardPopup-title": "Import Card",
+ "searchCardPopup-title": "Search Card",
"copyCardPopup-title": "Copy Card",
"copyChecklistToManyCardsPopup-title": "Copy Checklist Template to Many Cards",
"copyChecklistToManyCardsPopup-instructions": "Destination Card Titles and Descriptions in this JSON format",
diff --git a/models/boards.js b/models/boards.js
index c51a9865..eda34bf4 100644
--- a/models/boards.js
+++ b/models/boards.js
@@ -212,6 +212,10 @@ Boards.helpers({
return this.permission === 'public';
},
+ cards() {
+ return Cards.find({ boardId: this._id, archived: false }, { sort: { title: 1 } });
+ },
+
lists() {
return Lists.find({ boardId: this._id, archived: false }, { sort: { sort: 1 } });
},
diff --git a/models/cards.js b/models/cards.js
index b6a7b4c6..af8bea48 100644
--- a/models/cards.js
+++ b/models/cards.js
@@ -133,6 +133,13 @@ Cards.attachSchema(new SimpleSchema({
defaultValue: -1,
optional: true,
},
+ type: {
+ type: String,
+ },
+ importedId: {
+ type: String,
+ optional: true,
+ },
}));
Cards.allow({
diff --git a/server/migrations.js b/server/migrations.js
index 6135f1be..32f24e4c 100644
--- a/server/migrations.js
+++ b/server/migrations.js
@@ -213,6 +213,17 @@ Migrations.add('add-profile-view', () => {
});
});
+Migrations.add('add-card-types', () => {
+ Cards.find().forEach((card) => {
+ Cards.direct.update(
+ { _id: card._id },
+ { $set: {
+ type: 'cardType-card',
+ importedId: null } },
+ noValidate
+ );
+ });
+
Migrations.add('add-custom-fields-to-cards', () => {
Cards.update({
customFields: {