import.js 6.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234
  1. /// Abstract root for all import popup screens.
  2. /// Descendants must define:
  3. /// - getMethodName(): return the Meteor method to call for import, passing json
  4. /// data decoded as object and additional data (see below);
  5. /// - getAdditionalData(): return object containing additional data passed to
  6. /// Meteor method (like list ID and position for a card import);
  7. /// - getLabel(): i18n key for the text displayed in the popup, usually to
  8. /// explain how to get the data out of the source system.
  9. const ImportPopup = BlazeComponent.extendComponent({
  10. template() {
  11. return 'importPopup';
  12. },
  13. onCreated() {
  14. this.error = new ReactiveVar('');
  15. this.dataToImport = '';
  16. },
  17. onFinish() {
  18. Popup.close();
  19. },
  20. onSubmit(evt){
  21. evt.preventDefault();
  22. const dataJson = $(evt.currentTarget).find('.js-import-json').val();
  23. let dataObject;
  24. try {
  25. dataObject = JSON.parse(dataJson);
  26. this.setError('');
  27. } catch (e) {
  28. this.setError('error-json-malformed');
  29. return;
  30. }
  31. // if there are members listed in the import, we need to map them
  32. if(dataObject.members.length > 0) {
  33. // we will work on the list itself (an ordered array of POJO)
  34. // when a mapping is done, we add a 'wekan' field to the POJO representing the imported member
  35. const membersToMap = dataObject.members;
  36. // todo save initial import object - to save later, on mapping submission
  37. // this.data().toImport = dataObject;
  38. // auto-map based on username
  39. const wekanMembers = Users;
  40. membersToMap.forEach((importedMember) => {
  41. const wekanUser = Users.findOne({username: importedMember.username})
  42. if(wekanUser) {
  43. importedMember.wekan = wekanUser;
  44. }
  45. });
  46. // store members data and mapping in Session
  47. // (we go deep and 2-way, so storing in data context is not a viable option)
  48. Session.set('import.membersToMap', membersToMap);
  49. Popup.open('mapMembers')(evt);
  50. } else {
  51. Meteor.call(this.getMethodName(), dataObject, this.getAdditionalData(),
  52. (error, response) => {
  53. if (error) {
  54. this.setError(error.error);
  55. } else {
  56. // ensure will display what we just imported
  57. Filter.addException(response);
  58. this.onFinish(response);
  59. }
  60. }
  61. );
  62. }
  63. },
  64. events() {
  65. return [{
  66. submit: this.onSubmit,
  67. }];
  68. },
  69. setError(error) {
  70. this.error.set(error);
  71. },
  72. });
  73. ImportPopup.extendComponent({
  74. getAdditionalData() {
  75. const listId = this.currentData()._id;
  76. const selector = `#js-list-${this.currentData()._id} .js-minicard:first`;
  77. const firstCardDom = $(selector).get(0);
  78. const sortIndex = Utils.calculateIndex(null, firstCardDom).base;
  79. const result = {listId, sortIndex};
  80. return result;
  81. },
  82. getMethodName() {
  83. return 'importTrelloCard';
  84. },
  85. getLabel() {
  86. return 'import-card-trello-instruction';
  87. },
  88. }).register('listImportCardPopup');
  89. ImportPopup.extendComponent({
  90. getAdditionalData() {
  91. const result = {};
  92. return result;
  93. },
  94. getMethodName() {
  95. return 'importTrelloBoard';
  96. },
  97. getLabel() {
  98. return 'import-board-trello-instruction';
  99. },
  100. onFinish(response) {
  101. Utils.goBoardId(response);
  102. },
  103. }).register('boardImportBoardPopup');
  104. const ImportMapMembers = BlazeComponent.extendComponent({
  105. members() {
  106. return Session.get('import.membersToMap');
  107. },
  108. _refreshMembers(listOfMembers) {
  109. Session.set('import.membersToMap', listOfMembers);
  110. },
  111. /**
  112. * Will look into the list of members to import for the specified memberId,
  113. * then set its property to the supplied value.
  114. * If unset is true, it will remove the property from the rest of the list as well.
  115. *
  116. * use:
  117. * - memberId = null to use selected member
  118. * - value = null to unset a property
  119. * - unset = true to ensure property is only set on 1 member at a time
  120. */
  121. _setPropertyForMember(property, value, memberId, unset = false) {
  122. const listOfMembers = this.members();
  123. let finder = null;
  124. if(memberId) {
  125. finder = (member) => member.id === memberId;
  126. } else {
  127. finder = (member) => member.selected;
  128. }
  129. listOfMembers.forEach((member) => {
  130. if(finder(member)) {
  131. if(value !== null) {
  132. member[property] = true;
  133. } else {
  134. delete member[property];
  135. }
  136. if(!unset) {
  137. // we shortcut if we don't care about unsetting the others
  138. return false;
  139. }
  140. } else {
  141. if(unset) {
  142. delete member[property];
  143. }
  144. }
  145. return true;
  146. });
  147. // Session.get gives us a copy, we have to set it back so it sticks
  148. this._refreshMembers(listOfMembers);
  149. },
  150. setSelectedMember(memberId) {
  151. return this._setPropertyForMember('selected', true, memberId, true);
  152. },
  153. /**
  154. * returns the member with specified id,
  155. * or the selected member if memberId is not specified
  156. */
  157. getMember(memberId = null) {
  158. const allMembers = Session.get('import.membersToMap');
  159. let finder = null;
  160. if(memberId) {
  161. finder = (user) => user.id === memberId;
  162. } else {
  163. finder = (user) => user.selected;
  164. }
  165. return allMembers.find(finder);
  166. },
  167. mapSelectedMember(wekan) {
  168. return this._setPropertyForMember('wekan', wekan, null);
  169. },
  170. unmapMember(memberId){
  171. return this._setPropertyForMember('wekan', null, memberId);
  172. },
  173. });
  174. ImportMapMembers.extendComponent({
  175. onSelectMember(evt) {
  176. const memberToMap = this.currentData();
  177. this.setSelectedMember(memberToMap.id);
  178. console.log(`selected member#${memberToMap.id}`);
  179. Popup.open('mapMembersAdd')(evt);
  180. },
  181. onRemove(evt){
  182. const userId = this.currentData()._id;
  183. console.log(`confirm and then call unmapMember ${userId}`);
  184. },
  185. onSubmit(evt) {
  186. console.log("Mapping:");
  187. console.log(this.members());
  188. },
  189. events() {
  190. return [{
  191. 'submit': this.onSubmit,
  192. 'click .js-add-members': this.onSelectMember,
  193. 'click .js-member': this.onRemove,
  194. }];
  195. },
  196. }).register('mapMembersPopup');
  197. ImportMapMembers.extendComponent({
  198. //template() {
  199. // return "mapMembersAddPopup";
  200. //},
  201. onSelectUser(){
  202. const wekanUser = this.currentData();
  203. console.log(`clicked on ${wekanUser._id}`);
  204. console.log(wekanUser);
  205. //this.mapSelectedMember(this.currentData());
  206. },
  207. events() {
  208. return [{
  209. //'click .js-select-member': this.onSelectUser(),
  210. }];
  211. },
  212. onRendered() {
  213. console.log('rendered');
  214. // todo XXX why do I not focus??
  215. $('.js-map-member input').focus();
  216. },
  217. }).register('mapMembersAddPopup');