due_date_select.js 6.18 KB
Newer Older
Phil Hughes's avatar
Phil Hughes committed
1
/* global dateFormat */
2

3
import $ from 'jquery';
Phil Hughes's avatar
Phil Hughes committed
4
import Pikaday from 'pikaday';
5
import axios from './lib/utils/axios_utils';
Filipa Lacerda's avatar
Filipa Lacerda committed
6
import { parsePikadayDate, pikadayToString } from './lib/utils/datefix';
Jacob Schatz's avatar
Jacob Schatz committed
7

8 9 10 11 12 13 14 15 16 17 18 19 20
class DueDateSelect {
  constructor({ $dropdown, $loading } = {}) {
    const $dropdownParent = $dropdown.closest('.dropdown');
    const $block = $dropdown.closest('.block');
    this.$loading = $loading;
    this.$dropdown = $dropdown;
    this.$dropdownParent = $dropdownParent;
    this.$datePicker = $dropdownParent.find('.js-due-date-calendar');
    this.$block = $block;
    this.$selectbox = $dropdown.closest('.selectbox');
    this.$value = $block.find('.value');
    this.$valueContent = $block.find('.value-content');
    this.$sidebarValue = $('.js-due-date-sidebar-value', $block);
Jacob Schatz's avatar
Jacob Schatz committed
21 22 23
    this.fieldName = $dropdown.data('fieldName');
    this.abilityName = $dropdown.data('abilityName');
    this.issueUpdateURL = $dropdown.data('issueUpdate');
24 25 26 27 28 29 30 31 32

    this.rawSelectedDate = null;
    this.displayedDate = null;
    this.datePayload = null;

    this.initGlDropdown();
    this.initRemoveDueDate();
    this.initDatePicker();
  }
33

34 35 36 37 38 39 40 41 42
  initGlDropdown() {
    this.$dropdown.glDropdown({
      opened: () => {
        const calendar = this.$datePicker.data('pikaday');
        calendar.show();
      },
      hidden: () => {
        this.$selectbox.hide();
        this.$value.css('display', '');
Filipa Lacerda's avatar
Filipa Lacerda committed
43
      },
44 45
    });
  }
46

47 48 49 50 51 52
  initDatePicker() {
    const $dueDateInput = $(`input[name='${this.fieldName}']`);
    const calendar = new Pikaday({
      field: $dueDateInput.get(0),
      theme: 'gitlab-theme',
      format: 'yyyy-mm-dd',
Filipa Lacerda's avatar
Filipa Lacerda committed
53 54
      parse: dateString => parsePikadayDate(dateString),
      toString: date => pikadayToString(date),
55
      onSelect: (dateText) => {
Filipa Lacerda's avatar
Filipa Lacerda committed
56
        $dueDateInput.val(calendar.toString(dateText));
57

58
        if (this.$dropdown.hasClass('js-issue-boards-due-date')) {
59
          gl.issueBoards.BoardsStore.detail.issue.dueDate = $dueDateInput.val();
60 61
          this.updateIssueBoardIssue();
        } else {
62
          this.saveDueDate(true);
63
        }
Filipa Lacerda's avatar
Filipa Lacerda committed
64
      },
65
    });
66

Filipa Lacerda's avatar
Filipa Lacerda committed
67
    calendar.setDate(parsePikadayDate($dueDateInput.val()));
68 69 70 71 72 73 74 75
    this.$datePicker.append(calendar.el);
    this.$datePicker.data('pikaday', calendar);
  }

  initRemoveDueDate() {
    this.$block.on('click', '.js-remove-due-date', (e) => {
      const calendar = this.$datePicker.data('pikaday');
      e.preventDefault();
76

77
      calendar.setDate(null);
78

79 80 81
      if (this.$dropdown.hasClass('js-issue-boards-due-date')) {
        gl.issueBoards.BoardsStore.detail.issue.dueDate = '';
        this.updateIssueBoardIssue();
82
      } else {
Filipa Lacerda's avatar
Filipa Lacerda committed
83 84
        $(`input[name='${this.fieldName}']`).val('');
        this.saveDueDate(false);
85
      }
86 87
    });
  }
88

89 90 91 92 93
  saveDueDate(isDropdown) {
    this.parseSelectedDate();
    this.prepSelectedDate();
    this.submitSelectedDate(isDropdown);
  }
94

95 96
  parseSelectedDate() {
    this.rawSelectedDate = $(`input[name='${this.fieldName}']`).val();
97

98 99 100 101 102 103 104
    if (this.rawSelectedDate.length) {
      // Construct Date object manually to avoid buggy dateString support within Date constructor
      const dateArray = this.rawSelectedDate.split('-').map(v => parseInt(v, 10));
      const dateObj = new Date(dateArray[0], dateArray[1] - 1, dateArray[2]);
      this.displayedDate = dateFormat(dateObj, 'mmm d, yyyy');
    } else {
      this.displayedDate = 'No due date';
105
    }
106 107 108 109 110 111 112 113 114
  }

  prepSelectedDate() {
    const datePayload = {};
    datePayload[this.abilityName] = {};
    datePayload[this.abilityName].due_date = this.rawSelectedDate;
    this.datePayload = datePayload;
  }

Filipa Lacerda's avatar
Filipa Lacerda committed
115
  updateIssueBoardIssue() {
116 117 118 119
    this.$loading.fadeIn();
    this.$dropdown.trigger('loading.gl.dropdown');
    this.$selectbox.hide();
    this.$value.css('display', '');
120 121 122
    const fadeOutLoader = () => {
      this.$loading.fadeOut();
    };
123 124

    gl.issueBoards.BoardsStore.detail.issue.update(this.$dropdown.attr('data-issue-update'))
125 126
      .then(fadeOutLoader)
      .catch(fadeOutLoader);
127 128 129
  }

  submitSelectedDate(isDropdown) {
130 131
    const selectedDateValue = this.datePayload[this.abilityName].due_date;
    const displayedDateStyle = this.displayedDate !== 'No due date' ? 'bold' : 'no-value';
132

133
    this.$loading.removeClass('hidden').fadeIn();
134

135 136 137 138
    if (isDropdown) {
      this.$dropdown.trigger('loading.gl.dropdown');
      this.$selectbox.hide();
    }
139

140 141 142
    this.$value.css('display', '');
    this.$valueContent.html(`<span class='${displayedDateStyle}'>${this.displayedDate}</span>`);
    this.$sidebarValue.html(this.displayedDate);
143

144 145 146 147 148 149 150 151 152 153
    $('.js-remove-due-date-holder').toggleClass('hidden', selectedDateValue.length);

    return axios.put(this.issueUpdateURL, this.datePayload)
      .then(() => {
        if (isDropdown) {
          this.$dropdown.trigger('loaded.gl.dropdown');
          this.$dropdown.dropdown('toggle');
        }
        return this.$loading.fadeOut();
      });
154
  }
155
}
156

Filipa Lacerda's avatar
Filipa Lacerda committed
157
export default class DueDateSelectors {
158 159 160 161
  constructor() {
    this.initMilestoneDatePicker();
    this.initIssuableSelect();
  }
Filipa Lacerda's avatar
Filipa Lacerda committed
162
  // eslint-disable-next-line class-methods-use-this
163
  initMilestoneDatePicker() {
Filipa Lacerda's avatar
Filipa Lacerda committed
164
    $('.datepicker').each(function initPikadayMilestone() {
165 166 167
      const $datePicker = $(this);
      const calendar = new Pikaday({
        field: $datePicker.get(0),
168
        theme: 'gitlab-theme animate-picker',
169
        format: 'yyyy-mm-dd',
170
        container: $datePicker.parent().get(0),
Filipa Lacerda's avatar
Filipa Lacerda committed
171 172
        parse: dateString => parsePikadayDate(dateString),
        toString: date => pikadayToString(date),
173
        onSelect(dateText) {
Filipa Lacerda's avatar
Filipa Lacerda committed
174 175
          $datePicker.val(calendar.toString(dateText));
        },
176
      });
177

Filipa Lacerda's avatar
Filipa Lacerda committed
178
      calendar.setDate(parsePikadayDate($datePicker.val()));
179

180 181
      $datePicker.data('pikaday', calendar);
    });
182

183 184 185 186 187 188
    $('.js-clear-due-date,.js-clear-start-date').on('click', (e) => {
      e.preventDefault();
      const calendar = $(e.target).siblings('.datepicker').data('pikaday');
      calendar.setDate(null);
    });
  }
Filipa Lacerda's avatar
Filipa Lacerda committed
189
  // eslint-disable-next-line class-methods-use-this
190 191
  initIssuableSelect() {
    const $loading = $('.js-issuable-update .due_date').find('.block-loading').hide();
192

193 194
    $('.js-due-date-select').each((i, dropdown) => {
      const $dropdown = $(dropdown);
Filipa Lacerda's avatar
Filipa Lacerda committed
195
      // eslint-disable-next-line no-new
196 197
      new DueDateSelect({
        $dropdown,
Filipa Lacerda's avatar
Filipa Lacerda committed
198
        $loading,
199
      });
200
    });
201
  }
202
}