From 57f31d443faaa32d6c7b53d81af3be133af5f040 Mon Sep 17 00:00:00 2001 From: Lauri Ojansivu Date: Wed, 9 Jun 2021 00:51:52 +0300 Subject: [PATCH] Partial Fix: Vote and Planning Poker: Setting date and time now works for some languages that have ascii characters in date format. Thanks to xet7 ! Related #3837 --- client/components/cards/cardDetails.js | 386 +++++++++++++++++++++++-- 1 file changed, 356 insertions(+), 30 deletions(-) diff --git a/client/components/cards/cardDetails.js b/client/components/cards/cardDetails.js index a973df075..8345460f0 100644 --- a/client/components/cards/cardDetails.js +++ b/client/components/cards/cardDetails.js @@ -170,7 +170,7 @@ BlazeComponent.extendComponent({ }).fetch(); if (integrations.length > 0) { - integrations.forEach(integration => { + integrations.forEach((integration) => { Meteor.call( 'outgoingWebhooks', integration, @@ -336,9 +336,7 @@ BlazeComponent.extendComponent({ }, 'submit .js-card-details-title'(event) { event.preventDefault(); - const title = this.currentComponent() - .getValue() - .trim(); + const title = this.currentComponent().getValue().trim(); if (title) { this.data().setTitle(title); } else { @@ -347,9 +345,7 @@ BlazeComponent.extendComponent({ }, 'submit .js-card-details-assigner'(event) { event.preventDefault(); - const assigner = this.currentComponent() - .getValue() - .trim(); + const assigner = this.currentComponent().getValue().trim(); if (assigner) { this.data().setAssignedBy(assigner); } else { @@ -358,9 +354,7 @@ BlazeComponent.extendComponent({ }, 'submit .js-card-details-requester'(event) { event.preventDefault(); - const requester = this.currentComponent() - .getValue() - .trim(); + const requester = this.currentComponent().getValue().trim(); if (requester) { this.data().setRequestedBy(requester); } else { @@ -576,7 +570,7 @@ Template.cardDetailsActionsPopup.events({ const minOrder = _.min( this.list() .cards(this.swimlaneId) - .map(c => c.sort), + .map((c) => c.sort), ); this.move(this.boardId, this.swimlaneId, this.listId, minOrder - 1); }, @@ -585,7 +579,7 @@ Template.cardDetailsActionsPopup.events({ const maxOrder = _.max( this.list() .cards(this.swimlaneId) - .map(c => c.sort), + .map((c) => c.sort), ); this.move(this.boardId, this.swimlaneId, this.listId, maxOrder + 1); }, @@ -604,7 +598,7 @@ Template.cardDetailsActionsPopup.events({ }, }); -Template.editCardTitleForm.onRendered(function() { +Template.editCardTitleForm.onRendered(function () { autosize(this.$('.js-edit-card-title')); }); @@ -618,7 +612,7 @@ Template.editCardTitleForm.events({ }, }); -Template.editCardRequesterForm.onRendered(function() { +Template.editCardRequesterForm.onRendered(function () { autosize(this.$('.js-edit-card-requester')); }); @@ -631,7 +625,7 @@ Template.editCardRequesterForm.events({ }, }); -Template.editCardAssignerForm.onRendered(function() { +Template.editCardAssignerForm.onRendered(function () { autosize(this.$('.js-edit-card-assigner')); }); @@ -716,9 +710,7 @@ Template.copyCardPopup.events({ const textarea = $('#copy-card-title'); const title = textarea.val().trim(); // insert new card to the bottom of new list - card.sort = Lists.findOne(card.listId) - .cards() - .count(); + card.sort = Lists.findOne(card.listId).cards().count(); if (title) { card.title = title; @@ -749,9 +741,7 @@ Template.copyChecklistToManyCardsPopup.events({ const textarea = $('#copy-card-title'); const titleEntry = textarea.val().trim(); // insert new card to the bottom of new list - card.sort = Lists.findOne(card.listId) - .cards() - .count(); + card.sort = Lists.findOne(card.listId).cards().count(); if (titleEntry) { const titleList = JSON.parse(titleEntry); @@ -768,13 +758,13 @@ Template.copyChecklistToManyCardsPopup.events({ Filter.addException(_id); // copy checklists - Checklists.find({ cardId: oldId }).forEach(ch => { + Checklists.find({ cardId: oldId }).forEach((ch) => { ch.copy(_id); }); // copy subtasks const cursor = Cards.find({ parentId: oldId }); - cursor.forEach(function() { + cursor.forEach(function () { 'use strict'; const subtask = arguments[0]; subtask.parentId = _id; @@ -783,7 +773,7 @@ Template.copyChecklistToManyCardsPopup.events({ }); // copy card comments - CardComments.find({ cardId: oldId }).forEach(cmt => { + CardComments.find({ cardId: oldId }).forEach((cmt) => { cmt.copy(_id); }); } @@ -799,7 +789,7 @@ BlazeComponent.extendComponent({ }, colors() { - return ALLOWED_COLORS.map(color => ({ color, name: '' })); + return ALLOWED_COLORS.map((color) => ({ color, name: '' })); }, isSelected(color) { @@ -922,7 +912,7 @@ BlazeComponent.extendComponent({ } } }, - 'click .js-delete': Popup.afterConfirm('cardDelete', function() { + 'click .js-delete': Popup.afterConfirm('cardDelete', function () { Popup.close(); // verify that there are no linked cards if (Cards.find({ linkedId: this._id }).count() === 0) { @@ -1029,6 +1019,8 @@ BlazeComponent.extendComponent({ moment(new Date().setHours(12, 0, 0)).format('LT'); const dateString = `${evt.target.date.value} ${time}`; + + /* const newDate = moment(dateString, 'L LT', true); if (newDate.isValid()) { // if active vote - store it @@ -1039,6 +1031,159 @@ BlazeComponent.extendComponent({ this.currentData().vote = { end: newDate.toDate() }; // set vote end temp Popup.back(); } + + + */ + + // Try to parse different date formats of all languages. + // This code is same for vote and planning poker. + const usaDate = moment(dateString, 'L LT', true); + const euroAmDate = moment(dateString, 'DD.MM.YYYY LT', true); + const euro24hDate = moment(dateString, 'DD.MM.YYYY HH.mm', true); + const eurodotDate = moment(dateString, 'DD.MM.YYYY HH:mm', true); + const minusDate = moment(dateString, 'YYYY-MM-DD HH:mm', true); + const slashDate = moment(dateString, 'DD/MM/YYYY HH.mm', true); + const dotDate = moment(dateString, 'DD/MM/YYYY HH:mm', true); + const brezhonegDate = moment(dateString, 'DD/MM/YYYY h[e]mm A', true); + const hrvatskiDate = moment(dateString, 'DD. MM. YYYY H:mm', true); + const latviaDate = moment(dateString, 'YYYY.MM.DD. H:mm', true); + const nederlandsDate = moment(dateString, 'DD-MM-YYYY HH:mm', true); + // greekDate does not work: el Greek Ελληνικά , + // it has date format DD/MM/YYYY h:mm MM like 20/06/2021 11:15 MM + // where MM is maybe some text like AM/PM ? + // Also some other languages that have non-ascii characters in dates + // do not work. + const greekDate = moment(dateString, 'DD/MM/YYYY h:mm A', true); + const macedonianDate = moment(dateString, 'D.MM.YYYY H:mm', true); + + if (usaDate.isValid()) { + // if active poker - store it + if (this.currentData().getPokerQuestion()) { + this._storeDate(usaDate.toDate()); + Popup.close(); + } else { + this.currentData().poker = { end: usaDate.toDate() }; // set poker end temp + Popup.back(); + } + } else if (euroAmDate.isValid()) { + // if active poker - store it + if (this.currentData().getPokerQuestion()) { + this._storeDate(euroAmDate.toDate()); + Popup.close(); + } else { + this.currentData().poker = { end: euroAmDate.toDate() }; // set poker end temp + Popup.back(); + } + } else if (euro24hDate.isValid()) { + // if active poker - store it + if (this.currentData().getPokerQuestion()) { + this._storeDate(euro24hDate.toDate()); + this.card.setPokerEnd(euro24hDate.toDate()); + Popup.close(); + } else { + this.currentData().poker = { end: euro24hDate.toDate() }; // set poker end temp + Popup.back(); + } + } else if (eurodotDate.isValid()) { + // if active poker - store it + if (this.currentData().getPokerQuestion()) { + this._storeDate(eurodotDate.toDate()); + this.card.setPokerEnd(eurodotDate.toDate()); + Popup.close(); + } else { + this.currentData().poker = { end: eurodotDate.toDate() }; // set poker end temp + Popup.back(); + } + } else if (minusDate.isValid()) { + // if active poker - store it + if (this.currentData().getPokerQuestion()) { + this._storeDate(minusDate.toDate()); + this.card.setPokerEnd(minusDate.toDate()); + Popup.close(); + } else { + this.currentData().poker = { end: minusDate.toDate() }; // set poker end temp + Popup.back(); + } + } else if (slashDate.isValid()) { + // if active poker - store it + if (this.currentData().getPokerQuestion()) { + this._storeDate(slashDate.toDate()); + this.card.setPokerEnd(slashDate.toDate()); + Popup.close(); + } else { + this.currentData().poker = { end: slashDate.toDate() }; // set poker end temp + Popup.back(); + } + } else if (dotDate.isValid()) { + // if active poker - store it + if (this.currentData().getPokerQuestion()) { + this._storeDate(dotDate.toDate()); + this.card.setPokerEnd(dotDate.toDate()); + Popup.close(); + } else { + this.currentData().poker = { end: dotDate.toDate() }; // set poker end temp + Popup.back(); + } + } else if (brezhonegDate.isValid()) { + // if active poker - store it + if (this.currentData().getPokerQuestion()) { + this._storeDate(brezhonegDate.toDate()); + this.card.setPokerEnd(brezhonegDate.toDate()); + Popup.close(); + } else { + this.currentData().poker = { end: brezhonegDate.toDate() }; // set poker end temp + Popup.back(); + } + } else if (hrvatskiDate.isValid()) { + // if active poker - store it + if (this.currentData().getPokerQuestion()) { + this._storeDate(hrvatskiDate.toDate()); + this.card.setPokerEnd(hrvatskiDate.toDate()); + Popup.close(); + } else { + this.currentData().poker = { end: hrvatskiDate.toDate() }; // set poker end temp + Popup.back(); + } + } else if (latviaDate.isValid()) { + // if active poker - store it + if (this.currentData().getPokerQuestion()) { + this._storeDate(latviaDate.toDate()); + this.card.setPokerEnd(latviaDate.toDate()); + Popup.close(); + } else { + this.currentData().poker = { end: latviaDate.toDate() }; // set poker end temp + Popup.back(); + } + } else if (nederlandsDate.isValid()) { + // if active poker - store it + if (this.currentData().getPokerQuestion()) { + this._storeDate(nederlandsDate.toDate()); + this.card.setPokerEnd(nederlandsDate.toDate()); + Popup.close(); + } else { + this.currentData().poker = { end: nederlandsDate.toDate() }; // set poker end temp + Popup.back(); + } + } else if (greekDate.isValid()) { + // if active poker - store it + if (this.currentData().getPokerQuestion()) { + this._storeDate(greekDate.toDate()); + this.card.setPokerEnd(greekDate.toDate()); + Popup.close(); + } else { + this.currentData().poker = { end: greekDate.toDate() }; // set poker end temp + Popup.back(); + } + } else if (macedonianDate.isValid()) { + // if active poker - store it + if (this.currentData().getPokerQuestion()) { + this._storeDate(macedonianDate.toDate()); + this.card.setPokerEnd(macedonianDate.toDate()); + Popup.close(); + } else { + this.currentData().poker = { end: macedonianDate.toDate() }; // set poker end temp + Popup.back(); + } } else { this.error.set('invalid-date'); evt.target.date.focus(); @@ -1087,7 +1232,7 @@ BlazeComponent.extendComponent({ } Popup.close(); }, - 'click .js-remove-poker': Popup.afterConfirm('deletePoker', () => { + 'click .js-remove-poker': Popup.afterConfirm('deletePoker', (event) => { event.preventDefault(); this.currentCard.unsetPoker(); Popup.close(); @@ -1105,8 +1250,28 @@ BlazeComponent.extendComponent({ (class extends DatePicker { onCreated() { super.onCreated(moment().format('YYYY-MM-DD HH:mm')); - this.data().getPokerEnd() && this.date.set(moment(this.data().getPokerEnd())); + this.data().getPokerEnd() && + this.date.set(moment(this.data().getPokerEnd())); } + + /* + Tried to use dateFormat and timeFormat from client/components/lib/datepicker.js + to make detecting all date formats not necessary, + but got error "language mk does not exist". + Maybe client/components/lib/datepicker.jade could have hidden input field for + datepicker format that could be used to detect date format? + + dateFormat() { + return moment.localeData().longDateFormat('L'); + } + + timeFormat() { + return moment.localeData().longDateFormat('LT'); + } + + const newDate = moment(dateString, dateformat() + ' ' + timeformat(), true); + */ + events() { return [ { @@ -1119,7 +1284,16 @@ BlazeComponent.extendComponent({ moment(new Date().setHours(12, 0, 0)).format('LT'); const dateString = `${evt.target.date.value} ${time}`; - const newDate = moment(dateString, 'L LT', true); + + /* + Tried to use dateFormat and timeFormat from client/components/lib/datepicker.js + to make detecting all date formats not necessary, + but got error "language mk does not exist". + Maybe client/components/lib/datepicker.jade could have hidden input field for + datepicker format that could be used to detect date format? + + const newDate = moment(dateString, dateformat() + ' ' + timeformat(), true); + if (newDate.isValid()) { // if active poker - store it if (this.currentData().getPokerQuestion()) { @@ -1129,8 +1303,160 @@ BlazeComponent.extendComponent({ this.currentData().poker = { end: newDate.toDate() }; // set poker end temp Popup.back(); } + */ + + // Try to parse different date formats of all languages. + // This code is same for vote and planning poker. + const usaDate = moment(dateString, 'L LT', true); + const euroAmDate = moment(dateString, 'DD.MM.YYYY LT', true); + const euro24hDate = moment(dateString, 'DD.MM.YYYY HH.mm', true); + const eurodotDate = moment(dateString, 'DD.MM.YYYY HH:mm', true); + const minusDate = moment(dateString, 'YYYY-MM-DD HH:mm', true); + const slashDate = moment(dateString, 'DD/MM/YYYY HH.mm', true); + const dotDate = moment(dateString, 'DD/MM/YYYY HH:mm', true); + const brezhonegDate = moment(dateString, 'DD/MM/YYYY h[e]mm A', true); + const hrvatskiDate = moment(dateString, 'DD. MM. YYYY H:mm', true); + const latviaDate = moment(dateString, 'YYYY.MM.DD. H:mm', true); + const nederlandsDate = moment(dateString, 'DD-MM-YYYY HH:mm', true); + // greekDate does not work: el Greek Ελληνικά , + // it has date format DD/MM/YYYY h:mm MM like 20/06/2021 11:15 MM + // where MM is maybe some text like AM/PM ? + // Also some other languages that have non-ascii characters in dates + // do not work. + const greekDate = moment(dateString, 'DD/MM/YYYY h:mm A', true); + const macedonianDate = moment(dateString, 'D.MM.YYYY H:mm', true); + + if (usaDate.isValid()) { + // if active poker - store it + if (this.currentData().getPokerQuestion()) { + this._storeDate(usaDate.toDate()); + Popup.close(); + } else { + this.currentData().poker = { end: usaDate.toDate() }; // set poker end temp + Popup.back(); + } + } else if (euroAmDate.isValid()) { + // if active poker - store it + if (this.currentData().getPokerQuestion()) { + this._storeDate(euroAmDate.toDate()); + Popup.close(); + } else { + this.currentData().poker = { end: euroAmDate.toDate() }; // set poker end temp + Popup.back(); + } + } else if (euro24hDate.isValid()) { + // if active poker - store it + if (this.currentData().getPokerQuestion()) { + this._storeDate(euro24hDate.toDate()); + this.card.setPokerEnd(euro24hDate.toDate()); + Popup.close(); + } else { + this.currentData().poker = { end: euro24hDate.toDate() }; // set poker end temp + Popup.back(); + } + } else if (eurodotDate.isValid()) { + // if active poker - store it + if (this.currentData().getPokerQuestion()) { + this._storeDate(eurodotDate.toDate()); + this.card.setPokerEnd(eurodotDate.toDate()); + Popup.close(); + } else { + this.currentData().poker = { end: eurodotDate.toDate() }; // set poker end temp + Popup.back(); + } + } else if (minusDate.isValid()) { + // if active poker - store it + if (this.currentData().getPokerQuestion()) { + this._storeDate(minusDate.toDate()); + this.card.setPokerEnd(minusDate.toDate()); + Popup.close(); + } else { + this.currentData().poker = { end: minusDate.toDate() }; // set poker end temp + Popup.back(); + } + } else if (slashDate.isValid()) { + // if active poker - store it + if (this.currentData().getPokerQuestion()) { + this._storeDate(slashDate.toDate()); + this.card.setPokerEnd(slashDate.toDate()); + Popup.close(); + } else { + this.currentData().poker = { end: slashDate.toDate() }; // set poker end temp + Popup.back(); + } + } else if (dotDate.isValid()) { + // if active poker - store it + if (this.currentData().getPokerQuestion()) { + this._storeDate(dotDate.toDate()); + this.card.setPokerEnd(dotDate.toDate()); + Popup.close(); + } else { + this.currentData().poker = { end: dotDate.toDate() }; // set poker end temp + Popup.back(); + } + } else if (brezhonegDate.isValid()) { + // if active poker - store it + if (this.currentData().getPokerQuestion()) { + this._storeDate(brezhonegDate.toDate()); + this.card.setPokerEnd(brezhonegDate.toDate()); + Popup.close(); + } else { + this.currentData().poker = { end: brezhonegDate.toDate() }; // set poker end temp + Popup.back(); + } + } else if (hrvatskiDate.isValid()) { + // if active poker - store it + if (this.currentData().getPokerQuestion()) { + this._storeDate(hrvatskiDate.toDate()); + this.card.setPokerEnd(hrvatskiDate.toDate()); + Popup.close(); + } else { + this.currentData().poker = { end: hrvatskiDate.toDate() }; // set poker end temp + Popup.back(); + } + } else if (latviaDate.isValid()) { + // if active poker - store it + if (this.currentData().getPokerQuestion()) { + this._storeDate(latviaDate.toDate()); + this.card.setPokerEnd(latviaDate.toDate()); + Popup.close(); + } else { + this.currentData().poker = { end: latviaDate.toDate() }; // set poker end temp + Popup.back(); + } + } else if (nederlandsDate.isValid()) { + // if active poker - store it + if (this.currentData().getPokerQuestion()) { + this._storeDate(nederlandsDate.toDate()); + this.card.setPokerEnd(nederlandsDate.toDate()); + Popup.close(); + } else { + this.currentData().poker = { end: nederlandsDate.toDate() }; // set poker end temp + Popup.back(); + } + } else if (greekDate.isValid()) { + // if active poker - store it + if (this.currentData().getPokerQuestion()) { + this._storeDate(greekDate.toDate()); + this.card.setPokerEnd(greekDate.toDate()); + Popup.close(); + } else { + this.currentData().poker = { end: greekDate.toDate() }; // set poker end temp + Popup.back(); + } + } else if (macedonianDate.isValid()) { + // if active poker - store it + if (this.currentData().getPokerQuestion()) { + this._storeDate(macedonianDate.toDate()); + this.card.setPokerEnd(macedonianDate.toDate()); + Popup.close(); + } else { + this.currentData().poker = { end: macedonianDate.toDate() }; // set poker end temp + Popup.back(); + } } else { - this.error.set('invalid-date'); + // this.error.set('invalid-date); + this.error.set('invalid-date' + ' ' + dateString); evt.target.date.focus(); } },