From 3dc70b3ea4d49bccb10d4f2b3e317c794da91e37 Mon Sep 17 00:00:00 2001 From: Xavier Priour Date: Fri, 13 Nov 2015 12:35:41 +0100 Subject: Import members: UI flow ok --- client/components/import/import.jade | 45 ++++++- client/components/import/import.js | 212 ++++++++++++++++++++++++++------- client/components/import/import.styl | 13 ++ client/components/sidebar/sidebar.jade | 2 +- 4 files changed, 220 insertions(+), 52 deletions(-) create mode 100644 client/components/import/import.styl (limited to 'client/components') diff --git a/client/components/import/import.jade b/client/components/import/import.jade index 799834fa..07e1116b 100644 --- a/client/components/import/import.jade +++ b/client/components/import/import.jade @@ -7,9 +7,42 @@ template(name="importPopup") input.primary.wide(type="submit" value="{{_ 'import'}}") template(name="mapMembersPopup") - p {{_ 'import-members-map'}} - ul - each members - li.item {{ fullName }} > world - form - input.primary.wide(type="submit" value="{{_ 'import'}}") + .map-members + p {{_ 'import-members-map'}} + .mapping-list + each members + .mapping + a.source + div.full-name + = fullName + div.username + | ({{username}}) + .wekan + if wekan + +userAvatar(userId=wekan._id) + else + a.member.add-member.js-add-members + i.fa.fa-plus + form + input.primary.wide(type="submit" value="{{_ 'import'}}") + + template(name="addMemberPopup") + +template(name="mapMembersAddPopup") + .select-member + p Hello world + .js-map-member + +esInput(index="users") + ul.pop-over-list + +esEach(index="users") + li.item.js-member-item + a.name.js-select-member(title="{{profile.name}} ({{username}})" data-id="{{_id}}") + +userAvatar(userId=_id esSearch=true) + span.full-name + = profile.name + | ({{username}}) + +ifEsIsSearching(index='users') + +spinner + +ifEsHasNoResults(index="users") + .manage-member-section + p.quiet {{_ 'no-results'}} diff --git a/client/components/import/import.js b/client/components/import/import.js index f46f15bc..3682afdb 100644 --- a/client/components/import/import.js +++ b/client/components/import/import.js @@ -11,60 +11,76 @@ const ImportPopup = BlazeComponent.extendComponent({ return 'importPopup'; }, - events() { - return [{ - submit(evt) { - evt.preventDefault(); - const dataJson = $(evt.currentTarget).find('.js-import-json').val(); - let dataObject; - try { - dataObject = JSON.parse(dataJson); - this.setError(''); - } catch (e) { - this.setError('error-json-malformed'); - return; + onCreated() { + this.error = new ReactiveVar(''); + this.dataToImport = ''; + }, + + onFinish() { + Popup.close(); + }, + + onSubmit(evt){ + evt.preventDefault(); + const dataJson = $(evt.currentTarget).find('.js-import-json').val(); + let dataObject; + try { + dataObject = JSON.parse(dataJson); + this.setError(''); + } catch (e) { + this.setError('error-json-malformed'); + return; + } + // if there are members listed in the import, we need to map them + if(dataObject.members.length > 0) { + // we will work on the list itself (an ordered array of POJO) + // when a mapping is done, we add a 'wekan' field to the POJO representing the imported member + const membersToMap = dataObject.members; + // todo save initial import object - to save later, on mapping submission + // this.data().toImport = dataObject; + + // auto-map based on username + const wekanMembers = Users; + membersToMap.forEach((importedMember) => { + const wekanUser = Users.findOne({username: importedMember.username}) + if(wekanUser) { + importedMember.wekan = wekanUser; } - if(dataObject.members.length > 0) { - this.data().toImport = dataObject; - members.forEach( - // todo if there is a Wekan user with same name, add it as a field 'wekanUser' - ); - this.data().members = dataObject.members; - // we bind to preserve data context - Popup.open('mapMembers').bind(this)(evt); - } else { - Meteor.call(this.getMethodName(), dataObject, this.getAdditionalData(), - (error, response) => { - if (error) { - this.setError(error.error); - } else { - Filter.addException(response); - this.onFinish(response); - } - } - ); + }); + // store members data and mapping in Session + // (we go deep and 2-way, so storing in data context is not a viable option) + Session.set('import.membersToMap', membersToMap); + Popup.open('mapMembers')(evt); + } else { + Meteor.call(this.getMethodName(), dataObject, this.getAdditionalData(), + (error, response) => { + if (error) { + this.setError(error.error); + } else { + // ensure will display what we just imported + Filter.addException(response); + this.onFinish(response); + } } - }, - }]; + ); + } }, - onCreated() { - this.error = new ReactiveVar(''); - this.dataToImport = ''; + events() { + return [{ + submit: this.onSubmit, + }]; }, setError(error) { this.error.set(error); }, - onFinish() { - Popup.close(); - }, }); ImportPopup.extendComponent({ getAdditionalData() { - const listId = this.data()._id; + const listId = this.currentData()._id; const selector = `#js-list-${this.currentData()._id} .js-minicard:first`; const firstCardDom = $(selector).get(0); const sortIndex = Utils.calculateIndex(null, firstCardDom).base; @@ -100,13 +116,119 @@ ImportPopup.extendComponent({ }, }).register('boardImportBoardPopup'); -BlazeComponent.extendComponent({ +const ImportMapMembers = BlazeComponent.extendComponent({ + members() { + return Session.get('import.membersToMap'); + }, + _refreshMembers(listOfMembers) { + Session.set('import.membersToMap', listOfMembers); + }, + /** + * Will look into the list of members to import for the specified memberId, + * then set its property to the supplied value. + * If unset is true, it will remove the property from the rest of the list as well. + * + * use: + * - memberId = null to use selected member + * - value = null to unset a property + * - unset = true to ensure property is only set on 1 member at a time + */ + _setPropertyForMember(property, value, memberId, unset = false) { + const listOfMembers = this.members(); + let finder = null; + if(memberId) { + finder = (member) => member.id === memberId; + } else { + finder = (member) => member.selected; + } + listOfMembers.forEach((member) => { + if(finder(member)) { + if(value !== null) { + member[property] = true; + } else { + delete member[property]; + } + if(!unset) { + // we shortcut if we don't care about unsetting the others + return false; + } + } else { + if(unset) { + delete member[property]; + } + } + return true; + }); + // Session.get gives us a copy, we have to set it back so it sticks + this._refreshMembers(listOfMembers); + }, + setSelectedMember(memberId) { + return this._setPropertyForMember('selected', true, memberId, true); + }, + /** + * returns the member with specified id, + * or the selected member if memberId is not specified + */ + getMember(memberId = null) { + const allMembers = Session.get('import.membersToMap'); + let finder = null; + if(memberId) { + finder = (user) => user.id === memberId; + } else { + finder = (user) => user.selected; + } + return allMembers.find(finder); + }, + mapSelectedMember(wekan) { + return this._setPropertyForMember('wekan', wekan, null); + }, + unmapMember(memberId){ + return this._setPropertyForMember('wekan', null, memberId); + }, +}); + +ImportMapMembers.extendComponent({ + onSelectMember(evt) { + const memberToMap = this.currentData(); + this.setSelectedMember(memberToMap.id); + console.log(`selected member#${memberToMap.id}`); + Popup.open('mapMembersAdd')(evt); + }, + onRemove(evt){ + const userId = this.currentData()._id; + console.log(`confirm and then call unmapMember ${userId}`); + }, + onSubmit(evt) { + console.log("Mapping:"); + console.log(this.members()); + }, events() { return [{ - 'submit': (evt) => { - evt.preventDefault(); - console.log(this.data()); - }, + 'submit': this.onSubmit, + 'click .js-add-members': this.onSelectMember, + 'click .js-member': this.onRemove, }]; }, }).register('mapMembersPopup'); + +ImportMapMembers.extendComponent({ + //template() { + // return "mapMembersAddPopup"; + //}, + onSelectUser(){ + const wekanUser = this.currentData(); + console.log(`clicked on ${wekanUser._id}`); + console.log(wekanUser); + //this.mapSelectedMember(this.currentData()); + }, + events() { + return [{ + //'click .js-select-member': this.onSelectUser(), + }]; + }, + onRendered() { + console.log('rendered'); + // todo XXX why do I not focus?? + $('.js-map-member input').focus(); + }, +}).register('mapMembersAddPopup'); diff --git a/client/components/import/import.styl b/client/components/import/import.styl new file mode 100644 index 00000000..4934cbcc --- /dev/null +++ b/client/components/import/import.styl @@ -0,0 +1,13 @@ +.map-members + .mapping + margin-bottom: 10px + margin-top: 10px + border-bottom: solid + .source + display: inline-block + width: 80% + .wekan + display: inline-block + width: 35px + .member + float: none diff --git a/client/components/sidebar/sidebar.jade b/client/components/sidebar/sidebar.jade index 91047056..f98ea4ee 100644 --- a/client/components/sidebar/sidebar.jade +++ b/client/components/sidebar/sidebar.jade @@ -89,7 +89,7 @@ template(name="addMemberPopup") a.name.js-select-member(title="{{profile.name}} ({{username}})") +userAvatar(userId=_id esSearch=true) span.full-name - = profile.name + = profile.fullname | ({{username}}) if isBoardMember .quiet ({{_ 'joined'}}) -- cgit v1.2.3-1-g7c22