From 468694a84cc164e4923f2d2e4631c37ceb1c4b55 Mon Sep 17 00:00:00 2001 From: Xavier Priour Date: Thu, 15 Oct 2015 14:01:13 +0200 Subject: Import board: added UI --- client/components/boards/boardHeader.jade | 2 + client/components/boards/boardHeader.js | 1 + client/components/boards/boardHeader.styl | 2 + client/components/import/import.jade | 8 ++++ client/components/import/import.js | 80 +++++++++++++++++++++++++++++++ client/components/lists/listHeader.jade | 9 ---- client/components/lists/listHeader.js | 39 --------------- i18n/en.i18n.json | 8 +++- models/import.js | 25 ++++++++-- 9 files changed, 119 insertions(+), 55 deletions(-) create mode 100644 client/components/boards/boardHeader.styl create mode 100644 client/components/import/import.jade create mode 100644 client/components/import/import.js diff --git a/client/components/boards/boardHeader.jade b/client/components/boards/boardHeader.jade index ffc79143..e460170b 100644 --- a/client/components/boards/boardHeader.jade +++ b/client/components/boards/boardHeader.jade @@ -107,6 +107,8 @@ template(name="createBoardPopup") | {{{_ 'board-private-info'}}} a.js-change-visibility {{_ 'change'}}. input.primary.wide(type="submit" value="{{_ 'create'}}") + | {{_ 'or'}} + a.js-import {{_ 'import-board'}} template(name="boardChangeTitlePopup") diff --git a/client/components/boards/boardHeader.js b/client/components/boards/boardHeader.js index dbd76895..92d5f6d4 100644 --- a/client/components/boards/boardHeader.js +++ b/client/components/boards/boardHeader.js @@ -145,6 +145,7 @@ BlazeComponent.extendComponent({ this.setVisibility(this.currentData()); }, 'click .js-change-visibility': this.toggleVisibilityMenu, + 'click .js-import': Popup.open('boardImportBoard'), submit: this.onSubmit, }]; }, diff --git a/client/components/boards/boardHeader.styl b/client/components/boards/boardHeader.styl new file mode 100644 index 00000000..adfe4b19 --- /dev/null +++ b/client/components/boards/boardHeader.styl @@ -0,0 +1,2 @@ +a.js-import + text-decoration underline diff --git a/client/components/import/import.jade b/client/components/import/import.jade new file mode 100644 index 00000000..8059b65b --- /dev/null +++ b/client/components/import/import.jade @@ -0,0 +1,8 @@ +template(name="importPopup") + if error.get + .warning {{_ error.get}} + form + label + | {{_ getLabel}} + textarea.js-card-json(placeholder="{{_ 'import-json-placeholder'}}" autofocus) + input.primary.wide(type="submit" value="{{_ 'import'}}") diff --git a/client/components/import/import.js b/client/components/import/import.js new file mode 100644 index 00000000..f15185ed --- /dev/null +++ b/client/components/import/import.js @@ -0,0 +1,80 @@ +/** + * Abstract root for all import popup screens. + * Descendants must define: + * - getMethodName(): return the Meteor method to call for import, passing json data decoded as object + * and additional data (see below) + * - getAdditionalData(): return object containing additional data passed to Meteor method + * (like list ID and position for a card import) + * - getLabel(): i18n key for the text displayed in the popup, usually to explain how to get the data out of the + * source system. + */ +const ImportPopup = BlazeComponent.extendComponent({ + template() {return 'importPopup';}, + events() { + return [{ + 'submit': (evt) => { + evt.preventDefault(); + const dataJson = $(evt.currentTarget).find('textarea').val(); + let dataObject; + try { + dataObject = JSON.parse(dataJson); + } catch (e) { + this.setError('error-json-malformed'); + return; + } + Meteor.call(this.getMethodName(), dataObject, this.getAdditionalData(), + (error, response) => { + if (error) { + this.setError(error.error); + } else { + Filter.addException(response); + Popup.close(); + } + } + ); + }, + }]; + }, + + onCreated() { + this.error = new ReactiveVar(''); + }, + + setError(error) { + this.error.set(error); + }, +}); + +ImportPopup.extendComponent({ + getAdditionalData() { + const listId = this.data()._id; + const firstCardDom = $(`#js-list-${this.currentData()._id} .js-minicard:first`).get(0); + const sortIndex = Utils.calculateIndex(null, firstCardDom).base; + const result = {listId, sortIndex}; + return result; + }, + + getMethodName() { + return 'importTrelloCard'; + }, + + getLabel() { + return 'import-card-trello-instruction'; + }, +}).register('listImportCardPopup'); + +ImportPopup.extendComponent({ + getAdditionalData() { + const result = {}; + return result; + }, + + getMethodName() { + return 'importTrelloBoard'; + }, + + getLabel() { + return 'import-board-trello-instruction'; + }, +}).register('boardImportBoardPopup'); + diff --git a/client/components/lists/listHeader.jade b/client/components/lists/listHeader.jade index e7b16912..72cd0fe9 100644 --- a/client/components/lists/listHeader.jade +++ b/client/components/lists/listHeader.jade @@ -31,15 +31,6 @@ template(name="listActionPopup") template(name="listMoveCardsPopup") +boardLists -template(name="listImportCardPopup") - if error.get - .warning {{_ error.get}} - form - label - | {{_ 'card-json'}} - textarea.js-card-json(placeholder="{{_ 'card-json-placeholder'}}" autofocus) - input.primary.wide(type="submit" value="{{_ 'import'}}") - template(name="boardLists") ul.pop-over-list each currentBoard.lists diff --git a/client/components/lists/listHeader.js b/client/components/lists/listHeader.js index e34d23fd..4f5fc3a0 100644 --- a/client/components/lists/listHeader.js +++ b/client/components/lists/listHeader.js @@ -49,45 +49,6 @@ Template.listActionPopup.events({ }, }); - -BlazeComponent.extendComponent({ - events() { - return [{ - 'submit': (evt) => { - evt.preventDefault(); - const jsonData = $(evt.currentTarget).find('textarea').val(); - const firstCardDom = $(`#js-list-${this.currentData()._id} .js-minicard:first`).get(0); - const sortIndex = Utils.calculateIndex(null, firstCardDom).base; - let trelloCard; - try { - trelloCard = JSON.parse(jsonData); - } catch (e) { - this.setError('error-json-malformed'); - return; - } - Meteor.call('importTrelloCard', trelloCard, this.currentData()._id, sortIndex, - (error, response) => { - if (error) { - this.setError(error.error); - } else { - Filter.addException(response); - Popup.close(); - } - } - ); - }, - }]; - }, - - onCreated() { - this.error = new ReactiveVar(''); - }, - - setError(error) { - this.error.set(error); - }, -}).register('listImportCardPopup'); - Template.listMoveCardsPopup.events({ 'click .js-select-list'() { const fromList = Template.parentData(2).data; diff --git a/i18n/en.i18n.json b/i18n/en.i18n.json index efc6128d..82ea14e5 100644 --- a/i18n/en.i18n.json +++ b/i18n/en.i18n.json @@ -54,6 +54,7 @@ "boardChangeColorPopup-title": "Change Board Background", "boardChangeTitlePopup-title": "Rename Board", "boardChangeVisibilityPopup-title": "Change Visibility", + "boardImportBoardPopup-title": "Import board from Trello", "boardMenuPopup-title": "Board Menu", "boards": "Boards", "bucket-example": "Like “Bucket List” for example", @@ -66,8 +67,6 @@ "card-edit-attachments": "Edit attachments", "card-edit-labels": "Edit labels", "card-edit-members": "Edit members", - "card-json": "Go to a Trello card, select 'Share and more...' then 'Export JSON' and copy the resulting text", - "card-json-placeholder": "Paste your valid JSON data here", "card-labels-title": "Change the labels for the card.", "card-members-title": "Add or remove members of the board from the card.", "cardAttachmentsPopup-title": "Attach From", @@ -136,7 +135,11 @@ "header-logo-title": "Go back to your boards page.", "home": "Home", "import": "Import", + "import-board": "import from Trello", + "import-board-trello-instruction": "In your Trello board, go to 'Menu', then 'More', 'Print and Export', 'Export JSON', and copy the resulting text", "import-card": "Import a Trello card", + "import-card-trello-instruction": "Go to a Trello card, select 'Share and more...' then 'Export JSON' and copy the resulting text", + "import-json-placeholder": "Paste your valid JSON data here", "info": "Infos", "initials": "Initials", "joined": "joined", @@ -175,6 +178,7 @@ "normal": "Normal", "normal-desc": "Can view and edit cards. Can't change settings.", "optional": "optional", + "or": "or", "page-maybe-private": "This page may be private. You may be able to view it by logging in.", "page-not-found": "Page not found.", "password": "Password", diff --git a/models/import.js b/models/import.js index 4fe4b478..e81dd42a 100644 --- a/models/import.js +++ b/models/import.js @@ -1,5 +1,5 @@ Meteor.methods({ - importTrelloCard(trelloCard, listId, sortIndex) { + importTrelloCard(trelloCard, data) { // 1. check parameters are ok from a syntax point of view const DateString = Match.Where(function (dateAsString) { check(dateAsString, String); @@ -22,14 +22,16 @@ Meteor.methods({ })], members: [Object], })); - check(listId, String); - check(sortIndex, Number); + check(data, { + listId: String, + sortIndex: Number, + }); } catch(e) { throw new Meteor.Error('error-json-schema'); } // 2. check parameters are ok from a business point of view (exist & authorized) - const list = Lists.findOne(listId); + const list = Lists.findOne(data.listId); if(!list) { throw new Meteor.Error('error-list-doesNotExist'); } @@ -49,7 +51,7 @@ Meteor.methods({ dateLastActivity: dateOfImport, description: trelloCard.desc, listId: list._id, - sort: sortIndex, + sort: data.sortIndex, title: trelloCard.name, // XXX use the original user? userId: Meteor.userId(), @@ -127,4 +129,17 @@ Meteor.methods({ }); return cardId; }, + importTrelloBoard(trelloBoard, data) { + // 1. check parameters are ok from a syntax point of view + try { + // XXX do proper checking + check(trelloBoard, Object); + check(data, Object); + } catch(e) { + throw new Meteor.Error('error-json-schema'); + } + // 2. check parameters are ok from a business point of view (exist & authorized) + // XXX check we are allowed + // 3. create all elements + }, }); -- cgit v1.2.3-1-g7c22