summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--client/components/cards/cardDate.jade20
-rw-r--r--client/components/cards/cardDate.js193
-rw-r--r--client/components/cards/cardDate.styl50
-rw-r--r--client/components/cards/cardDetails.jade13
-rw-r--r--client/components/cards/cardDetails.js2
-rw-r--r--client/components/cards/cardDetails.styl7
-rwxr-xr-xi18n/en.i18n.json6
7 files changed, 290 insertions, 1 deletions
diff --git a/client/components/cards/cardDate.jade b/client/components/cards/cardDate.jade
new file mode 100644
index 00000000..ca074879
--- /dev/null
+++ b/client/components/cards/cardDate.jade
@@ -0,0 +1,20 @@
+template(name="editCardDate")
+ .edit-card-date
+ form.edit-date
+ .fields
+ .left
+ label(for="date") {{_ 'date'}}
+ input.js-date-field#date(type="text" name="date" value=showDate placeholder=dateFormat autofocus)
+ .right
+ label(for="time") {{_ 'time'}}
+ input.js-time-field#time(type="text" name="time" value=showTime placeholder=timeFormat)
+ .js-datepicker
+ if error.get
+ .warning {{_ error.get}}
+ button.primary.wide.left.js-submit-date(type="submit") {{_ 'save'}}
+ button.js-delete-date.negate.wide.right.js-delete-date {{_ 'delete'}}
+
+template(name="dateBadge")
+ a.js-edit-date.card-date(title="{{showTitle}}")
+ time(datetime="{{showISODate}}")
+ | {{showDate}}
diff --git a/client/components/cards/cardDate.js b/client/components/cards/cardDate.js
new file mode 100644
index 00000000..4a24941d
--- /dev/null
+++ b/client/components/cards/cardDate.js
@@ -0,0 +1,193 @@
+// Edit start & due dates
+const EditCardDate = BlazeComponent.extendComponent({
+ template() {
+ return 'editCardDate';
+ },
+
+ onCreated() {
+ this.error = new ReactiveVar('');
+ this.card = this.data();
+ this.date = new ReactiveVar(moment.invalid());
+ },
+
+ onRendered() {
+ let $picker = this.$('.js-datepicker').datepicker({
+ todayHighlight: true,
+ todayBtn: 'linked',
+ language: TAPi18n.getLanguage()
+ }).on('changeDate', function(e) {
+ const localDate = moment(e.date).format('L');
+ date.value = localDate;
+ this.error.set('');
+ time.focus();
+ }.bind(this));
+
+ if (this.date.get().isValid()) {
+ $picker.datepicker('update', this.date.get().toDate());
+ }
+ },
+
+ showDate() {
+ if (this.date.get().isValid())
+ return this.date.get().format('L');
+ },
+ showTime() {
+ if (this.date.get().isValid())
+ return this.date.get().format('LT');
+ },
+ dateFormat() {
+ return moment.localeData().longDateFormat('L');
+ },
+ timeFormat() {
+ return moment.localeData().longDateFormat('LT');
+ },
+
+ events() {
+ return [{
+ 'keyup .js-date-field'(evt) {
+ // parse for localized date format in strict mode
+ const dateMoment = moment(date.value, 'L', true);
+ if (dateMoment.isValid()) {
+ this.error.set('');
+ this.$('.js-datepicker').datepicker('update', dateMoment.toDate());
+ }
+ },
+ 'keyup .js-time-field'(evt) {
+ // parse for localized time format in strict mode
+ const dateMoment = moment(time.value, 'LT', true);
+ if (dateMoment.isValid()) {
+ this.error.set('');
+ }
+ },
+ 'submit .edit-date'(evt) {
+ evt.preventDefault();
+
+ // if no time was given, init with 12:00
+ var time = evt.target.time.value || moment(new Date().setHours(12,0,0)).format('LT');
+
+ const dateString = evt.target.date.value + ' ' + time;
+ const newDate = moment.utc(dateString, 'L LT', true);
+ if (newDate.isValid()) {
+ this._storeDate(newDate.toDate());
+ Popup.close();
+ }
+ else {
+ this.error.set('invalid-date');
+ evt.target.date.focus();
+ }
+ },
+ 'click .js-delete-date'(evt) {
+ evt.preventDefault();
+ this._deleteDate();
+ Popup.close();
+ },
+ }];
+ },
+});
+
+// editCardStartDatePopup
+(class extends EditCardDate {
+ onCreated() {
+ super();
+ if (this.data().startAt) {
+ this.date.set(moment.utc(this.data().startAt));
+ }
+ }
+
+ _storeDate(date) {
+ this.card.setStart(date);
+ }
+
+ _deleteDate() {
+ this.card.unsetStart();
+ }
+}).register('editCardStartDatePopup');
+
+// editCardDueDatePopup
+(class extends EditCardDate {
+ onCreated() {
+ super();
+ if (this.data().dueAt !== undefined) {
+ this.date.set(moment.utc(this.data().dueAt));
+ }
+ }
+
+ onRendered() {
+ super();
+ if (moment.isDate(this.card.startAt)) {
+ this.$('.js-datepicker').datepicker('setStartDate', this.card.startAt);
+ }
+ }
+
+ _storeDate(date) {
+ this.card.setDue(date);
+ }
+
+ _deleteDate() {
+ this.card.unsetDue();
+ }
+}).register('editCardDueDatePopup');
+
+
+
+// Display start & due dates
+const CardDate = BlazeComponent.extendComponent({
+ template() {
+ return 'dateBadge';
+ },
+
+ onCreated() {
+ this.date = ReactiveVar();
+ },
+
+ showDate() {
+ // this will start working once mquandalle:moment
+ // is updated to at least moment.js 2.10.5
+ // until then, the date is displayed in the "L" format
+ return this.date.get().calendar(null, {
+ sameElse: 'llll'
+ });
+ },
+
+ showTitle() {
+ return this.date.get().format('LLLL');
+ },
+
+ showISODate() {
+ return this.date.get().toISOString();
+ },
+});
+
+// cardStartDate
+(class extends CardDate {
+ onCreated() {
+ super();
+ let self = this;
+ this.autorun(() => {
+ self.date.set(moment.utc(this.data().startAt));
+ });
+ }
+
+ events() {
+ return super.events().concat({
+ 'click .js-edit-date': Popup.open('editCardStartDate'),
+ });
+ }
+}).register('cardStartDate');
+
+// cardDueDate
+(class extends CardDate {
+ onCreated() {
+ super();
+ let self = this;
+ this.autorun(() => {
+ self.date.set(moment.utc(this.data().dueAt));
+ });
+ }
+
+ events() {
+ return super.events().concat({
+ 'click .js-edit-date': Popup.open('editCardDueDate'),
+ });
+ }
+}).register('cardDueDate');
diff --git a/client/components/cards/cardDate.styl b/client/components/cards/cardDate.styl
new file mode 100644
index 00000000..e9d56f79
--- /dev/null
+++ b/client/components/cards/cardDate.styl
@@ -0,0 +1,50 @@
+.edit-card-date
+ .fields
+ .left
+ width: 56%
+ .right
+ width: 38%
+ .datepicker
+ width: 100%
+ table
+ width: 100%
+ border: none
+ border-spacing: 0
+ border-collapse: collapse
+ thead
+ background: none
+ td, th
+ box-sizing: border-box
+
+
+.card-date
+ display: block
+ border-radius: 4px
+ padding: 1px 3px;
+
+ background-color: #dbdbdb
+ &:hover, &.is-active
+ background-color: #b3b3b3
+
+ &.current
+ background-color: #42ca00
+ &:hover, &.is-active
+ background-color: darken(#42ca00, 15)
+
+ &.almost-due
+ background-color: #fad900
+ &:hover, &.is-active
+ background-color: darken(#fad900, 15)
+
+ &.due
+ background-color: #fa3f00
+ &:hover, &.is-active
+ background-color: darken(#fa3f00, 15)
+
+ time
+ &::before
+ font: normal normal normal 14px/1 FontAwesome
+ font-size: inherit
+ -webkit-font-smoothing: antialiased
+ content: "\f017" // clock symbol
+ margin-right: 0.3em \ No newline at end of file
diff --git a/client/components/cards/cardDetails.jade b/client/components/cards/cardDetails.jade
index 734fc7e3..69725d76 100644
--- a/client/components/cards/cardDetails.jade
+++ b/client/components/cards/cardDetails.jade
@@ -35,6 +35,17 @@ template(name="cardDetails")
a.card-label.add-label.js-add-labels(title="{{_ 'card-labels-title'}}")
i.fa.fa-plus
+ if startAt
+ .card-details-item.card-details-item-start
+ h3.card-details-item-title {{_ 'start-at'}}
+ +cardStartDate
+
+ if dueAt
+ .card-details-item.card-details-item-due
+ h3.card-details-item-title {{_ 'due-at'}}
+ +cardDueDate
+
+
//- XXX We should use "editable" to avoid repetiting ourselves
if currentUser.isBoardMember
h3.card-details-item-title {{_ 'description'}}
@@ -91,6 +102,8 @@ template(name="cardDetailsActionsPopup")
li: a.js-members {{_ 'card-edit-members'}}
li: a.js-labels {{_ 'card-edit-labels'}}
li: a.js-attachments {{_ 'card-edit-attachments'}}
+ li: a.js-start-date {{_ 'editCardStartDatePopup-title'}}
+ li: a.js-due-date {{_ 'editCardDueDatePopup-title'}}
hr
ul.pop-over-list
li: a.js-move-card-to-top {{_ 'moveCardToTop-title'}}
diff --git a/client/components/cards/cardDetails.js b/client/components/cards/cardDetails.js
index b6f17c23..32980926 100644
--- a/client/components/cards/cardDetails.js
+++ b/client/components/cards/cardDetails.js
@@ -143,6 +143,8 @@ Template.cardDetailsActionsPopup.events({
'click .js-members': Popup.open('cardMembers'),
'click .js-labels': Popup.open('cardLabels'),
'click .js-attachments': Popup.open('cardAttachments'),
+ 'click .js-start-date': Popup.open('editCardStartDate'),
+ 'click .js-due-date': Popup.open('editCardDueDate'),
'click .js-move-card': Popup.open('moveCard'),
'click .js-move-card-to-top'(evt) {
evt.preventDefault();
diff --git a/client/components/cards/cardDetails.styl b/client/components/cards/cardDetails.styl
index d7d29551..f209862c 100644
--- a/client/components/cards/cardDetails.styl
+++ b/client/components/cards/cardDetails.styl
@@ -73,8 +73,13 @@
margin: 15px 0
.card-details-item
+ margin-right: 0.5em
+ &:last-child
+ margin-right: 0
&.card-details-item-labels,
- &.card-details-item-members
+ &.card-details-item-members,
+ &.card-details-item-start,
+ &.card-details-item-due
width: 50%
flex-shrink: 1
diff --git a/i18n/en.i18n.json b/i18n/en.i18n.json
index 6e2098c4..fc7ae24b 100755
--- a/i18n/en.i18n.json
+++ b/i18n/en.i18n.json
@@ -133,6 +133,7 @@
"createBoardPopup-title": "Create Board",
"createLabelPopup-title": "Create Label",
"current": "current",
+ "date": "Date",
"decline": "Decline",
"default-avatar": "Default avatar",
"delete": "Delete",
@@ -143,9 +144,12 @@
"discard": "Discard",
"done": "Done",
"download": "Download",
+ "due-at": "Due",
"edit": "Edit",
"edit-avatar": "Change Avatar",
"edit-profile": "Edit Profile",
+ "editCardStartDatePopup-title": "Change start date",
+ "editCardDueDatePopup-title": "Change due date",
"editLabelPopup-title": "Change Label",
"editNotificationPopup-title": "Edit Notification",
"editProfilePopup-title": "Edit Profile",
@@ -282,10 +286,12 @@
"star-board-title": "Click to star this board. It will show up at top of your boards list.",
"starred-boards": "Starred Boards",
"starred-boards-description": "Starred boards show up at the top of your boards list.",
+ "start-at": "Start",
"subscribe": "Subscribe",
"team": "Team",
"this-board": "this board",
"this-card": "this card",
+ "time": "Time",
"title": "Title",
"tracking": "Tracking",
"tracking-info": "You will be notified of any changes to those cards you are involved as creator or member.",