Преглед изворни кода

Add basic start and due dates for cards.

shoetten пре 9 година
родитељ
комит
d4497d7aeb

+ 20 - 0
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}}

+ 193 - 0
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');

+ 50 - 0
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

+ 13 - 0
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'}}

+ 2 - 0
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();

+ 6 - 1
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
 

+ 6 - 0
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.",