import.js 6.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252
  1. import trelloMembersMapper from './trelloMembersMapper';
  2. import wekanMembersMapper from './wekanMembersMapper';
  3. BlazeComponent.extendComponent({
  4. title() {
  5. return `import-board-title-${Session.get('importSource')}`;
  6. },
  7. }).register('importHeaderBar');
  8. BlazeComponent.extendComponent({
  9. onCreated() {
  10. this.error = new ReactiveVar('');
  11. this.steps = ['importTextarea', 'importMapMembers'];
  12. this._currentStepIndex = new ReactiveVar(0);
  13. this.importedData = new ReactiveVar();
  14. this.membersToMap = new ReactiveVar([]);
  15. this.importSource = Session.get('importSource');
  16. },
  17. currentTemplate() {
  18. return this.steps[this._currentStepIndex.get()];
  19. },
  20. nextStep() {
  21. const nextStepIndex = this._currentStepIndex.get() + 1;
  22. if (nextStepIndex >= this.steps.length) {
  23. this.finishImport();
  24. } else {
  25. this._currentStepIndex.set(nextStepIndex);
  26. }
  27. },
  28. importData(evt) {
  29. evt.preventDefault();
  30. const dataJson = this.find('.js-import-json').value;
  31. try {
  32. const dataObject = JSON.parse(dataJson);
  33. this.setError('');
  34. this.importedData.set(dataObject);
  35. const membersToMap = this._prepareAdditionalData(dataObject);
  36. // store members data and mapping in Session
  37. // (we go deep and 2-way, so storing in data context is not a viable option)
  38. this.membersToMap.set(membersToMap);
  39. this.nextStep();
  40. } catch (e) {
  41. this.setError('error-json-malformed');
  42. }
  43. },
  44. setError(error) {
  45. this.error.set(error);
  46. },
  47. finishImport() {
  48. const additionalData = {};
  49. const membersMapping = this.membersToMap.get();
  50. if (membersMapping) {
  51. const mappingById = {};
  52. membersMapping.forEach(member => {
  53. if (member.wekanId) {
  54. mappingById[member.id] = member.wekanId;
  55. }
  56. });
  57. additionalData.membersMapping = mappingById;
  58. }
  59. this.membersToMap.set([]);
  60. Meteor.call(
  61. 'importBoard',
  62. this.importedData.get(),
  63. additionalData,
  64. this.importSource,
  65. Session.get('fromBoard'),
  66. (err, res) => {
  67. if (err) {
  68. this.setError(err.error);
  69. } else {
  70. Session.set('fromBoard', null);
  71. Utils.goBoardId(res);
  72. }
  73. },
  74. );
  75. },
  76. _prepareAdditionalData(dataObject) {
  77. const importSource = Session.get('importSource');
  78. let membersToMap;
  79. switch (importSource) {
  80. case 'trello':
  81. membersToMap = trelloMembersMapper.getMembersToMap(dataObject);
  82. break;
  83. case 'wekan':
  84. membersToMap = wekanMembersMapper.getMembersToMap(dataObject);
  85. break;
  86. }
  87. return membersToMap;
  88. },
  89. _screenAdditionalData() {
  90. return 'mapMembers';
  91. },
  92. }).register('import');
  93. BlazeComponent.extendComponent({
  94. template() {
  95. return 'importTextarea';
  96. },
  97. instruction() {
  98. return `import-board-instruction-${Session.get('importSource')}`;
  99. },
  100. events() {
  101. return [
  102. {
  103. submit(evt) {
  104. return this.parentComponent().importData(evt);
  105. },
  106. },
  107. ];
  108. },
  109. }).register('importTextarea');
  110. BlazeComponent.extendComponent({
  111. onCreated() {
  112. this.autorun(() => {
  113. this.parentComponent()
  114. .membersToMap.get()
  115. .forEach(({ wekanId }) => {
  116. if (wekanId) {
  117. this.subscribe('user-miniprofile', wekanId);
  118. }
  119. });
  120. });
  121. },
  122. members() {
  123. return this.parentComponent().membersToMap.get();
  124. },
  125. _refreshMembers(listOfMembers) {
  126. return this.parentComponent().membersToMap.set(listOfMembers);
  127. },
  128. /**
  129. * Will look into the list of members to import for the specified memberId,
  130. * then set its property to the supplied value.
  131. * If unset is true, it will remove the property from the rest of the list as well.
  132. *
  133. * use:
  134. * - memberId = null to use selected member
  135. * - value = null to unset a property
  136. * - unset = true to ensure property is only set on 1 member at a time
  137. */
  138. _setPropertyForMember(property, value, memberId, unset = false) {
  139. const listOfMembers = this.members();
  140. let finder = null;
  141. if (memberId) {
  142. finder = member => member.id === memberId;
  143. } else {
  144. finder = member => member.selected;
  145. }
  146. listOfMembers.forEach(member => {
  147. if (finder(member)) {
  148. if (value !== null) {
  149. member[property] = value;
  150. } else {
  151. delete member[property];
  152. }
  153. if (!unset) {
  154. // we shortcut if we don't care about unsetting the others
  155. return false;
  156. }
  157. } else if (unset) {
  158. delete member[property];
  159. }
  160. return true;
  161. });
  162. // Session.get gives us a copy, we have to set it back so it sticks
  163. this._refreshMembers(listOfMembers);
  164. },
  165. setSelectedMember(memberId) {
  166. return this._setPropertyForMember('selected', true, memberId, true);
  167. },
  168. /**
  169. * returns the member with specified id,
  170. * or the selected member if memberId is not specified
  171. */
  172. getMember(memberId = null) {
  173. const allMembers = this.members();
  174. let finder = null;
  175. if (memberId) {
  176. finder = user => user.id === memberId;
  177. } else {
  178. finder = user => user.selected;
  179. }
  180. return allMembers.find(finder);
  181. },
  182. mapSelectedMember(wekanId) {
  183. return this._setPropertyForMember('wekanId', wekanId, null);
  184. },
  185. unmapMember(memberId) {
  186. return this._setPropertyForMember('wekanId', null, memberId);
  187. },
  188. onSubmit(evt) {
  189. evt.preventDefault();
  190. this.parentComponent().nextStep();
  191. },
  192. onMapMember(evt) {
  193. const memberToMap = this.currentData();
  194. if (memberToMap.wekan) {
  195. // todo xxx ask for confirmation?
  196. this.unmapMember(memberToMap.id);
  197. } else {
  198. this.setSelectedMember(memberToMap.id);
  199. Popup.open('importMapMembersAdd')(evt);
  200. }
  201. },
  202. events() {
  203. return [
  204. {
  205. submit: this.onSubmit,
  206. 'click .js-select-member': this.onMapMember,
  207. },
  208. ];
  209. },
  210. }).register('importMapMembers');
  211. BlazeComponent.extendComponent({
  212. onRendered() {
  213. this.find('.js-map-member input').focus();
  214. },
  215. onSelectUser() {
  216. Popup.getOpenerComponent().mapSelectedMember(this.currentData()._id);
  217. Popup.back();
  218. },
  219. events() {
  220. return [
  221. {
  222. 'click .js-select-import': this.onSelectUser,
  223. },
  224. ];
  225. },
  226. }).register('importMapMembersAddPopup');