/src/Dime/ReportBundle/Resources/public/js/app/report/table.js

https://github.com/phpugl/Dime · JavaScript · 291 lines · 254 code · 25 blank · 12 comment · 56 complexity · 7cd9beba6ecfc42b7f2127099851ffd5 MD5 · raw file

  1. 'use strict';
  2. /**
  3. * Dime - app/report/table.js
  4. */
  5. (function ($, Backbone, _, App) {
  6. /**
  7. * App.Helper.Format.DurationNumber
  8. *
  9. * @param data, a duration in given unit, e.g. seconds
  10. * @param format, format with http://numeraljs.com/
  11. * @return string formatted as Number
  12. */
  13. App.provide('Helper.Format.DurationNumber', function (data, format) {
  14. if (data !== undefined && _.isNumber(data)) {
  15. var duration = (data / 60) / 60;
  16. if (format != undefined) {
  17. return numeral(duration).format(format);
  18. }
  19. return duration;
  20. }
  21. return '';
  22. });
  23. App.provide('Model.Report.Timeslice', Backbone.Model.extend({
  24. duration: function(precision, unit) {
  25. var duration = this.get('duration');
  26. if (precision && unit) {
  27. var base = 0;
  28. switch (unit) {
  29. case 's':
  30. base = 1;
  31. break;
  32. case 'm':
  33. base = 60;
  34. break;
  35. case 'h':
  36. base = 3600;
  37. break;
  38. }
  39. if (base > 0) {
  40. if (precision > 0 && precision < 60) {
  41. var part = base * precision,
  42. future = Math.floor(duration / part) * part,
  43. second = (duration % part);
  44. if (second > 0 && second < part) {
  45. future += part;
  46. }
  47. duration = future;
  48. } else {
  49. App.notify('Precision out of range [1,60]', 'error');
  50. }
  51. }
  52. }
  53. return duration;
  54. },
  55. fuzzyStart: function(precision, unit) {
  56. switch (unit) {
  57. case 's':
  58. return this.get('start').seconds(this.get('start').seconds() - this.get('start').seconds() % precision);
  59. case 'm':
  60. return this.get('start').minutes(this.get('start').minutes() - this.get('start').minutes() % precision)
  61. .seconds(0);
  62. case 'h':
  63. return this.get('start').hours(this.get('start').hours() - this.get('start').hours() % precision)
  64. .minutes(0)
  65. .seconds(0);
  66. default:
  67. return this.get('start');
  68. }
  69. },
  70. fuzzyStop: function(precision, unit) {
  71. return this.fuzzyStart(precision, unit).add('seconds', this.duration(precision, unit));
  72. },
  73. formatDuration:function (precision, unit) {
  74. return App.Helper.Format.Duration(this.duration(precision, unit));
  75. }
  76. }));
  77. App.provide('Collection.Report.Timeslices', Backbone.Collection.extend({
  78. duration: function(precision, unit) {
  79. var duration = 0;
  80. this.each(function(item) {
  81. if (item.get('duration')) {
  82. duration += item.duration(precision, unit)
  83. }
  84. });
  85. return duration;
  86. },
  87. formatDuration:function (precision, unit) {
  88. return App.Helper.Format.Duration(this.duration(precision, unit));
  89. }
  90. }));
  91. App.provide('Views.Report.Table', Backbone.View.extend({
  92. template: '#tpl-report-table',
  93. el: '#report-results',
  94. events: {
  95. 'click .save-tags': 'tagEntities'
  96. },
  97. initialize: function() {
  98. if (this.collection) {
  99. this.collection.on('add', this.render, this);
  100. this.collection.on('reset', this.render, this);
  101. }
  102. this.reset();
  103. this.tableOption = {};
  104. },
  105. reset: function() {
  106. this.timeslices = new App.Collection.Report.Timeslices();
  107. this.timeslices.comparator = function(first, second) {
  108. var firstDate = first.get('start'),
  109. secondDate = second.get('start');
  110. if (firstDate && secondDate) {
  111. if (firstDate.unix() > secondDate.unix()) {
  112. return 1;
  113. } else if (firstDate.unix() < secondDate.unix()) {
  114. return -1;
  115. } else {
  116. return 0;
  117. }
  118. } else {
  119. return 1;
  120. }
  121. };
  122. },
  123. setTableOptions: function(options) {
  124. this.tableOption = options;
  125. if (this.tableOption && this.tableOption.order) {
  126. this.tableOption.order = this.tableOption.order.split(',');
  127. }
  128. },
  129. render: function() {
  130. var that = this;
  131. if (this.timeslices.length > 0) {
  132. this.reset();
  133. }
  134. if (this.collection && this.collection.length > 0) {
  135. this.collection.each(function(model) {
  136. if (model && model.get('duration') && model.get('duration') > 0) {
  137. var tags = [];
  138. // Merge tags of Activities and Timeslices
  139. if (that.tableOption.tags) {
  140. _.each(that.tableOption.tags, function(item) {
  141. switch (item) {
  142. case 'activity':
  143. tags = _.union(tags, model.getRelation('activity').getRelation('tags').tagArray());
  144. break;
  145. case 'timeslice':
  146. tags = _.union(tags, model.getRelation('tags').tagArray());
  147. break;
  148. }
  149. });
  150. }
  151. if (that.tableOption.merged) {
  152. switch (that.tableOption.merged) {
  153. case 'date':
  154. if (model.get('startedAt')) {
  155. var date = moment(model.get('startedAt'), 'YYYY-MM-DD HH:mm:ss');
  156. var ts = that.timeslices.get(date.format('YYYY-MM-DD'));
  157. if (ts) {
  158. ts.set('duration', ts.get('duration') + model.get('duration'));
  159. } else {
  160. ts = new App.Model.Report.Timeslice({
  161. id: date.format('YYYY-MM-DD'),
  162. date: date,
  163. description: model.get('activity.description', ''),
  164. duration: model.get('duration'),
  165. customerName: model.get('activity.customer.name', undefined),
  166. projectName: model.get('activity.project.name', undefined),
  167. tags: tags
  168. });
  169. that.timeslices.add(ts);
  170. }
  171. }
  172. break;
  173. case 'description':
  174. var ts = that.timeslices.get(model.get('activity.id'));
  175. if (ts) {
  176. ts.set('duration', ts.get('duration') + model.get('duration'));
  177. } else {
  178. ts = new App.Model.Report.Timeslice({
  179. id: model.get('activity.id'),
  180. description: model.get('activity.description', ''),
  181. duration: model.get('duration'),
  182. customerName: model.get('activity.customer.name', undefined),
  183. projectName: model.get('activity.project.name', undefined),
  184. tags: tags
  185. });
  186. that.timeslices.add(ts);
  187. }
  188. break;
  189. }
  190. } else {
  191. var date = model.get('startedAt');
  192. if (!date) {
  193. date = model.get('createdAt');
  194. }
  195. that.timeslices.add(new App.Model.Report.Timeslice({
  196. date: moment(date, 'YYYY-MM-DD HH:mm:ss'),
  197. start: model.get('startedAt') ? moment(model.get('startedAt'), 'YYYY-MM-DD HH:mm:ss') : undefined,
  198. stop: model.get('stoppedAt') ? moment(model.get('stoppedAt'), 'YYYY-MM-DD HH:mm:ss') : undefined,
  199. description: model.get('activity.description', ''),
  200. duration: model.get('duration'),
  201. customerName: model.get('activity.customer.name', undefined),
  202. projectName: model.get('activity.project.name', undefined),
  203. tags: tags
  204. }));
  205. }
  206. }
  207. });
  208. this.$el.html(App.render(this.template, {opt: this.tableOption}));
  209. }
  210. this.update();
  211. return this;
  212. },
  213. update: function() {
  214. var that = this,
  215. thead = this.$('thead tr'),
  216. tbody = this.$('tbody'),
  217. tfoot = this.$('tfoot');
  218. thead.html('');
  219. _.each(this.tableOption.order, function(item) {
  220. thead.append(App.render(that.template + '-th-' + item, { opt: that.tableOption }));
  221. }, this);
  222. // reset
  223. tbody.html('');
  224. this.timeslices.each(function (model) {
  225. tbody.append(App.render(that.template + '-data', { opt: that.tableOption, model: model }));
  226. });
  227. var pos = _.indexOf(this.tableOption.order, 'duration');
  228. if (pos === -1) {
  229. pos = _.indexOf(this.tableOption.order, 'durationNumber');
  230. }
  231. if (pos > -1) {
  232. tfoot.html(
  233. App.render(that.template + '-tfoot', {
  234. opt: that.tableOption,
  235. duration: this.timeslices.duration(this.tableOption.precision, this.tableOption.precisionUnit),
  236. cols: pos
  237. })
  238. );
  239. }
  240. },
  241. tagEntities: function(e) {
  242. if (e) {
  243. e.stopPropagation();
  244. }
  245. var data = App.Helper.UI.Form.Serialize(this.$('#massive-tagging'), true);
  246. if (this.collection && data && data.tags) {
  247. var tags = App.Helper.Tags.Split(data.tags),
  248. activities = {};
  249. this.collection.each(function(model) {
  250. if (model && model.get('duration') && model.get('duration') > 0) {
  251. if (data.what === 'all' || data.what === 'timeslices') {
  252. App.Helper.Tags.Update(model, tags);
  253. }
  254. if (data.what === 'all' || data.what === 'activities') {
  255. var activitiy = model.getRelation('activity');
  256. if (!activities[activitiy.id]) {
  257. activities[activitiy.id] = true;
  258. App.Helper.Tags.Update(activitiy, tags);
  259. }
  260. }
  261. }
  262. }, this);
  263. }
  264. }
  265. }));
  266. })(jQuery, Backbone, _, Dime);