cardDate.js 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462
  1. import { TAPi18n } from '/imports/i18n';
  2. import { DatePicker } from '/client/lib/datepicker';
  3. import {
  4. formatDateTime,
  5. formatDate,
  6. formatDateByUserPreference,
  7. formatTime,
  8. getISOWeek,
  9. isValidDate,
  10. isBefore,
  11. isAfter,
  12. isSame,
  13. add,
  14. subtract,
  15. startOf,
  16. endOf,
  17. format,
  18. parseDate,
  19. now,
  20. createDate,
  21. fromNow,
  22. calendar,
  23. diff
  24. } from '/imports/lib/dateUtils';
  25. // editCardReceivedDatePopup
  26. (class extends DatePicker {
  27. onCreated() {
  28. super.onCreated(formatDateTime(now()));
  29. this.data().getReceived() &&
  30. this.date.set(new Date(this.data().getReceived()));
  31. }
  32. _storeDate(date) {
  33. this.card.setReceived(formatDateTime(date));
  34. }
  35. _deleteDate() {
  36. this.card.unsetReceived();
  37. }
  38. }.register('editCardReceivedDatePopup'));
  39. // editCardStartDatePopup
  40. (class extends DatePicker {
  41. onCreated() {
  42. super.onCreated(formatDateTime(now()));
  43. this.data().getStart() && this.date.set(new Date(this.data().getStart()));
  44. }
  45. onRendered() {
  46. super.onRendered();
  47. // DatePicker base class handles initialization with native HTML inputs
  48. }
  49. _storeDate(date) {
  50. this.card.setStart(formatDateTime(date));
  51. }
  52. _deleteDate() {
  53. this.card.unsetStart();
  54. }
  55. }.register('editCardStartDatePopup'));
  56. // editCardDueDatePopup
  57. (class extends DatePicker {
  58. onCreated() {
  59. super.onCreated('1970-01-01 17:00:00');
  60. this.data().getDue() && this.date.set(new Date(this.data().getDue()));
  61. }
  62. onRendered() {
  63. super.onRendered();
  64. // DatePicker base class handles initialization with native HTML inputs
  65. }
  66. _storeDate(date) {
  67. this.card.setDue(formatDateTime(date));
  68. }
  69. _deleteDate() {
  70. this.card.unsetDue();
  71. }
  72. }.register('editCardDueDatePopup'));
  73. // editCardEndDatePopup
  74. (class extends DatePicker {
  75. onCreated() {
  76. super.onCreated(formatDateTime(now()));
  77. this.data().getEnd() && this.date.set(new Date(this.data().getEnd()));
  78. }
  79. onRendered() {
  80. super.onRendered();
  81. // DatePicker base class handles initialization with native HTML inputs
  82. }
  83. _storeDate(date) {
  84. this.card.setEnd(formatDateTime(date));
  85. }
  86. _deleteDate() {
  87. this.card.unsetEnd();
  88. }
  89. }.register('editCardEndDatePopup'));
  90. // Display received, start, due & end dates
  91. const CardDate = BlazeComponent.extendComponent({
  92. template() {
  93. return 'dateBadge';
  94. },
  95. onCreated() {
  96. const self = this;
  97. self.date = ReactiveVar();
  98. self.now = ReactiveVar(now());
  99. window.setInterval(() => {
  100. self.now.set(now());
  101. }, 60000);
  102. },
  103. showWeek() {
  104. return getISOWeek(this.date.get()).toString();
  105. },
  106. showWeekOfYear() {
  107. const user = ReactiveCache.getCurrentUser();
  108. if (!user) {
  109. // For non-logged-in users, week of year is not shown
  110. return false;
  111. }
  112. return user.isShowWeekOfYear();
  113. },
  114. showDate() {
  115. const currentUser = ReactiveCache.getCurrentUser();
  116. const dateFormat = currentUser ? currentUser.getDateFormat() : 'YYYY-MM-DD';
  117. return formatDateByUserPreference(this.date.get(), dateFormat, true);
  118. },
  119. showISODate() {
  120. return this.date.get().toISOString();
  121. },
  122. });
  123. class CardReceivedDate extends CardDate {
  124. onCreated() {
  125. super.onCreated();
  126. const self = this;
  127. self.autorun(() => {
  128. self.date.set(new Date(self.data().getReceived()));
  129. });
  130. }
  131. classes() {
  132. let classes = 'received-date ';
  133. const dueAt = this.data().getDue();
  134. const endAt = this.data().getEnd();
  135. const startAt = this.data().getStart();
  136. const theDate = this.date.get();
  137. // if dueAt, endAt and startAt exist & are > receivedAt, receivedAt doesn't need to be flagged
  138. if (
  139. (startAt && isAfter(theDate, startAt)) ||
  140. (endAt && isAfter(theDate, endAt)) ||
  141. (dueAt && isAfter(theDate, dueAt))
  142. )
  143. classes += 'long-overdue';
  144. else classes += 'current';
  145. return classes;
  146. }
  147. showTitle() {
  148. const currentUser = ReactiveCache.getCurrentUser();
  149. const dateFormat = currentUser ? currentUser.getDateFormat() : 'YYYY-MM-DD';
  150. const formattedDate = formatDateByUserPreference(this.date.get(), dateFormat, true);
  151. return `${TAPi18n.__('card-received-on')} ${formattedDate}`;
  152. }
  153. events() {
  154. return super.events().concat({
  155. 'click .js-edit-date': Popup.open('editCardReceivedDate'),
  156. });
  157. }
  158. }
  159. CardReceivedDate.register('cardReceivedDate');
  160. class CardStartDate extends CardDate {
  161. onCreated() {
  162. super.onCreated();
  163. const self = this;
  164. self.autorun(() => {
  165. self.date.set(new Date(self.data().getStart()));
  166. });
  167. }
  168. classes() {
  169. let classes = 'start-date' + ' ';
  170. const dueAt = this.data().getDue();
  171. const endAt = this.data().getEnd();
  172. const theDate = this.date.get();
  173. const now = this.now.get();
  174. // if dueAt or endAt exist & are > startAt, startAt doesn't need to be flagged
  175. if ((endAt && isAfter(theDate, endAt)) || (dueAt && isAfter(theDate, dueAt)))
  176. classes += 'long-overdue';
  177. else if (isAfter(theDate, now)) classes += '';
  178. else classes += 'current';
  179. return classes;
  180. }
  181. showTitle() {
  182. const currentUser = ReactiveCache.getCurrentUser();
  183. const dateFormat = currentUser ? currentUser.getDateFormat() : 'YYYY-MM-DD';
  184. const formattedDate = formatDateByUserPreference(this.date.get(), dateFormat, true);
  185. return `${TAPi18n.__('card-start-on')} ${formattedDate}`;
  186. }
  187. events() {
  188. return super.events().concat({
  189. 'click .js-edit-date': Popup.open('editCardStartDate'),
  190. });
  191. }
  192. }
  193. CardStartDate.register('cardStartDate');
  194. class CardDueDate extends CardDate {
  195. onCreated() {
  196. super.onCreated();
  197. const self = this;
  198. self.autorun(() => {
  199. self.date.set(new Date(self.data().getDue()));
  200. });
  201. }
  202. classes() {
  203. let classes = 'due-date' + ' ';
  204. const endAt = this.data().getEnd();
  205. const theDate = this.date.get();
  206. const now = this.now.get();
  207. // if the due date is after the end date, green - done early
  208. if (endAt && isAfter(theDate, endAt)) classes += 'current';
  209. // if there is an end date, don't need to flag the due date
  210. else if (endAt) classes += '';
  211. else if (diff(now, theDate, 'days') >= 2) classes += 'long-overdue';
  212. else if (diff(now, theDate, 'minute') >= 0) classes += 'due';
  213. else if (diff(now, theDate, 'days') >= -1) classes += 'almost-due';
  214. return classes;
  215. }
  216. showTitle() {
  217. const currentUser = ReactiveCache.getCurrentUser();
  218. const dateFormat = currentUser ? currentUser.getDateFormat() : 'YYYY-MM-DD';
  219. const formattedDate = formatDateByUserPreference(this.date.get(), dateFormat, true);
  220. return `${TAPi18n.__('card-due-on')} ${formattedDate}`;
  221. }
  222. events() {
  223. return super.events().concat({
  224. 'click .js-edit-date': Popup.open('editCardDueDate'),
  225. });
  226. }
  227. }
  228. CardDueDate.register('cardDueDate');
  229. class CardEndDate extends CardDate {
  230. onCreated() {
  231. super.onCreated();
  232. const self = this;
  233. self.autorun(() => {
  234. self.date.set(new Date(self.data().getEnd()));
  235. });
  236. }
  237. classes() {
  238. let classes = 'end-date' + ' ';
  239. const dueAt = this.data().getDue();
  240. const theDate = this.date.get();
  241. if (!dueAt) classes += '';
  242. else if (isBefore(theDate, dueAt)) classes += 'current';
  243. else if (isAfter(theDate, dueAt)) classes += 'due';
  244. return classes;
  245. }
  246. showTitle() {
  247. return `${TAPi18n.__('card-end-on')} ${format(this.date.get(), 'LLLL')}`;
  248. }
  249. events() {
  250. return super.events().concat({
  251. 'click .js-edit-date': Popup.open('editCardEndDate'),
  252. });
  253. }
  254. }
  255. CardEndDate.register('cardEndDate');
  256. class CardCustomFieldDate extends CardDate {
  257. template() {
  258. return 'dateCustomField';
  259. }
  260. onCreated() {
  261. super.onCreated();
  262. const self = this;
  263. self.autorun(() => {
  264. self.date.set(new Date(self.data().value));
  265. });
  266. }
  267. showWeek() {
  268. return getISOWeek(this.date.get()).toString();
  269. }
  270. showWeekOfYear() {
  271. const user = ReactiveCache.getCurrentUser();
  272. if (!user) {
  273. // For non-logged-in users, week of year is not shown
  274. return false;
  275. }
  276. return user.isShowWeekOfYear();
  277. }
  278. showDate() {
  279. // this will start working once mquandalle:moment
  280. // is updated to at least moment.js 2.10.5
  281. // until then, the date is displayed in the "L" format
  282. return this.date.get().calendar(null, {
  283. sameElse: 'llll',
  284. });
  285. }
  286. showTitle() {
  287. const currentUser = ReactiveCache.getCurrentUser();
  288. const dateFormat = currentUser ? currentUser.getDateFormat() : 'YYYY-MM-DD';
  289. const formattedDate = formatDateByUserPreference(this.date.get(), dateFormat, true);
  290. return `${formattedDate}`;
  291. }
  292. classes() {
  293. return 'customfield-date';
  294. }
  295. events() {
  296. return [];
  297. }
  298. }
  299. CardCustomFieldDate.register('cardCustomFieldDate');
  300. (class extends CardReceivedDate {
  301. template() {
  302. return 'minicardReceivedDate';
  303. }
  304. showDate() {
  305. const currentUser = ReactiveCache.getCurrentUser();
  306. const dateFormat = currentUser ? currentUser.getDateFormat() : 'YYYY-MM-DD';
  307. return formatDateByUserPreference(this.date.get(), dateFormat, true);
  308. }
  309. }.register('minicardReceivedDate'));
  310. (class extends CardStartDate {
  311. template() {
  312. return 'minicardStartDate';
  313. }
  314. showDate() {
  315. const currentUser = ReactiveCache.getCurrentUser();
  316. const dateFormat = currentUser ? currentUser.getDateFormat() : 'YYYY-MM-DD';
  317. return formatDateByUserPreference(this.date.get(), dateFormat, true);
  318. }
  319. }.register('minicardStartDate'));
  320. (class extends CardDueDate {
  321. template() {
  322. return 'minicardDueDate';
  323. }
  324. showDate() {
  325. const currentUser = ReactiveCache.getCurrentUser();
  326. const dateFormat = currentUser ? currentUser.getDateFormat() : 'YYYY-MM-DD';
  327. return formatDateByUserPreference(this.date.get(), dateFormat, true);
  328. }
  329. }.register('minicardDueDate'));
  330. (class extends CardEndDate {
  331. template() {
  332. return 'minicardEndDate';
  333. }
  334. showDate() {
  335. const currentUser = ReactiveCache.getCurrentUser();
  336. const dateFormat = currentUser ? currentUser.getDateFormat() : 'YYYY-MM-DD';
  337. return formatDateByUserPreference(this.date.get(), dateFormat, true);
  338. }
  339. }.register('minicardEndDate'));
  340. (class extends CardCustomFieldDate {
  341. template() {
  342. return 'minicardCustomFieldDate';
  343. }
  344. showDate() {
  345. const currentUser = ReactiveCache.getCurrentUser();
  346. const dateFormat = currentUser ? currentUser.getDateFormat() : 'YYYY-MM-DD';
  347. return formatDateByUserPreference(this.date.get(), dateFormat, true);
  348. }
  349. }.register('minicardCustomFieldDate'));
  350. class VoteEndDate extends CardDate {
  351. onCreated() {
  352. super.onCreated();
  353. const self = this;
  354. self.autorun(() => {
  355. self.date.set(new Date(self.data().getVoteEnd()));
  356. });
  357. }
  358. classes() {
  359. const classes = 'end-date' + ' ';
  360. return classes;
  361. }
  362. showDate() {
  363. const currentUser = ReactiveCache.getCurrentUser();
  364. const dateFormat = currentUser ? currentUser.getDateFormat() : 'YYYY-MM-DD';
  365. return formatDateByUserPreference(this.date.get(), dateFormat, true);
  366. }
  367. showTitle() {
  368. return `${TAPi18n.__('card-end-on')} ${this.date.get().toLocaleString()}`;
  369. }
  370. events() {
  371. return super.events().concat({
  372. 'click .js-edit-date': Popup.open('editVoteEndDate'),
  373. });
  374. }
  375. }
  376. VoteEndDate.register('voteEndDate');
  377. class PokerEndDate extends CardDate {
  378. onCreated() {
  379. super.onCreated();
  380. const self = this;
  381. self.autorun(() => {
  382. self.date.set(new Date(self.data().getPokerEnd()));
  383. });
  384. }
  385. classes() {
  386. const classes = 'end-date' + ' ';
  387. return classes;
  388. }
  389. showDate() {
  390. const currentUser = ReactiveCache.getCurrentUser();
  391. const dateFormat = currentUser ? currentUser.getDateFormat() : 'YYYY-MM-DD';
  392. return formatDateByUserPreference(this.date.get(), dateFormat, true);
  393. }
  394. showTitle() {
  395. return `${TAPi18n.__('card-end-on')} ${format(this.date.get(), 'LLLL')}`;
  396. }
  397. events() {
  398. return super.events().concat({
  399. 'click .js-edit-date': Popup.open('editPokerEndDate'),
  400. });
  401. }
  402. }
  403. PokerEndDate.register('pokerEndDate');