milestone_select.js 9.4 KB
Newer Older
1
/* eslint-disable one-var, no-unused-vars, object-shorthand, no-else-return, no-self-compare, consistent-return, no-param-reassign, no-shadow */
2 3
/* global Issuable */
/* global ListMilestone */
4 5

import $ from 'jquery';
6
import _ from 'underscore';
7
import { __ } from '~/locale';
8
import '~/gl_dropdown';
9
import axios from './lib/utils/axios_utils';
10
import { timeFor } from './lib/utils/datetime_utility';
11
import ModalStore from './boards/stores/modal_store';
12 13 14 15
import boardsStore, {
  boardStoreIssueSet,
  boardStoreIssueDelete,
} from './boards/stores/boards_store';
16

17 18 19
export default class MilestoneSelect {
  constructor(currentProject, els, options = {}) {
    if (currentProject !== null) {
20 21
      this.currentProject =
        typeof currentProject === 'string' ? JSON.parse(currentProject) : currentProject;
22
    }
Phil Hughes's avatar
Phil Hughes committed
23

24
    MilestoneSelect.init(els, options);
25
  }
Phil Hughes's avatar
Phil Hughes committed
26

27
  static init(els, options) {
28
    let $els = $(els);
Phil Hughes's avatar
Phil Hughes committed
29

30 31 32
    if (!els) {
      $els = $('.js-milestone-select');
    }
33

34
    $els.each((i, dropdown) => {
35 36 37 38
      let milestoneLinkNoneTemplate,
        milestoneLinkTemplate,
        selectedMilestone,
        selectedMilestoneDefault;
39
      const $dropdown = $(dropdown);
Jacob Schatz's avatar
Jacob Schatz committed
40
      const projectId = $dropdown.data('projectId');
41 42
      const milestonesUrl = $dropdown.data('milestones');
      const issueUpdateURL = $dropdown.data('issueUpdate');
Jacob Schatz's avatar
Jacob Schatz committed
43 44
      const showNo = $dropdown.data('showNo');
      const showAny = $dropdown.data('showAny');
45
      const showMenuAbove = $dropdown.data('showMenuAbove');
Jacob Schatz's avatar
Jacob Schatz committed
46 47 48 49 50 51 52
      const showUpcoming = $dropdown.data('showUpcoming');
      const showStarted = $dropdown.data('showStarted');
      const useId = $dropdown.data('useId');
      const defaultLabel = $dropdown.data('defaultLabel');
      const defaultNo = $dropdown.data('defaultNo');
      const issuableId = $dropdown.data('issuableId');
      const abilityName = $dropdown.data('abilityName');
53 54 55 56 57
      const $selectBox = $dropdown.closest('.selectbox');
      const $block = $selectBox.closest('.block');
      const $sidebarCollapsedValue = $block.find('.sidebar-collapsed-icon');
      const $value = $block.find('.value');
      const $loading = $block.find('.block-loading').fadeOut();
58
      selectedMilestoneDefault = showAny ? '' : null;
59 60
      selectedMilestoneDefault =
        showNo && defaultNo ? __('No Milestone') : selectedMilestoneDefault;
61
      selectedMilestone = $dropdown.data('selected') || selectedMilestoneDefault;
62

63
      if (issueUpdateURL) {
64
        milestoneLinkTemplate = _.template(
65
          '<a href="<%- web_url %>" class="bold has-tooltip" data-container="body" title="<%- remaining %>"><%- title %></a>',
66
        );
67
        milestoneLinkNoneTemplate = `<span class="no-value">${__('None')}</span>`;
68 69 70
      }
      return $dropdown.glDropdown({
        showMenuAbove: showMenuAbove,
71 72
        data: (term, callback) =>
          axios.get(milestonesUrl).then(({ data }) => {
73 74 75
            const extraOptions = [];
            if (showAny) {
              extraOptions.push({
76 77
                id: null,
                name: null,
78
                title: __('Any Milestone'),
79 80 81 82 83
              });
            }
            if (showNo) {
              extraOptions.push({
                id: -1,
84 85
                name: __('No Milestone'),
                title: __('No Milestone'),
86 87 88 89 90 91
              });
            }
            if (showUpcoming) {
              extraOptions.push({
                id: -2,
                name: '#upcoming',
92
                title: __('Upcoming'),
93 94 95 96 97 98
              });
            }
            if (showStarted) {
              extraOptions.push({
                id: -3,
                name: '#started',
99
                title: __('Started'),
100 101 102 103 104
              });
            }
            if (extraOptions.length) {
              extraOptions.push('divider');
            }
105

106 107 108 109
            callback(extraOptions.concat(data));
            if (showMenuAbove) {
              $dropdown.data('glDropdown').positionMenuAbove();
            }
110
            $(`[data-milestone-id="${_.escape(selectedMilestone)}"] > a`).addClass('is-active');
111
          }),
112
        renderRow: milestone => `
113
          <li data-milestone-id="${_.escape(milestone.name)}">
114 115 116 117 118 119 120
            <a href='#' class='dropdown-menu-milestone-link'>
              ${_.escape(milestone.title)}
            </a>
          </li>
        `,
        filterable: true,
        search: {
121
          fields: ['title'],
122 123 124 125 126 127 128 129 130 131
        },
        selectable: true,
        toggleLabel: (selected, el, e) => {
          if (selected && 'id' in selected && $(el).hasClass('is-active')) {
            return selected.title;
          } else {
            return defaultLabel;
          }
        },
        defaultLabel: defaultLabel,
Jacob Schatz's avatar
Jacob Schatz committed
132
        fieldName: $dropdown.data('fieldName'),
133
        text: milestone => _.escape(milestone.title),
134
        id: milestone => {
135 136 137 138 139 140 141 142 143 144 145
          if (!useId && !$dropdown.is('.js-issuable-form-dropdown')) {
            return milestone.name;
          } else {
            return milestone.id;
          }
        },
        hidden: () => {
          $selectBox.hide();
          // display:block overrides the hide-collapse rule
          return $value.css('display', '');
        },
146
        opened: e => {
147 148 149 150 151
          const $el = $(e.currentTarget);
          if ($dropdown.hasClass('js-issue-board-sidebar') || options.handleClick) {
            selectedMilestone = $dropdown[0].dataset.selected || selectedMilestoneDefault;
          }
          $('a.is-active', $el).removeClass('is-active');
152
          $(`[data-milestone-id="${_.escape(selectedMilestone)}"] > a`, $el).addClass('is-active');
153 154
        },
        vue: $dropdown.hasClass('js-issue-board-sidebar'),
155
        clicked: clickEvent => {
156 157
          const { $el, e } = clickEvent;
          let selected = clickEvent.selectedObj;
158

159
          let data, modalStoreFilter;
160
          if (!selected) return;
Phil Hughes's avatar
Phil Hughes committed
161

162 163 164 165 166
          if (options.handleClick) {
            e.preventDefault();
            options.handleClick(selected);
            return;
          }
Phil Hughes's avatar
Phil Hughes committed
167

168 169
          const page = $('body').attr('data-page');
          const isIssueIndex = page === 'projects:issues:index';
170 171
          const isMRIndex = page === page && page === 'projects:merge_requests:index';
          const isSelecting = selected.name !== selectedMilestone;
172
          selectedMilestone = isSelecting ? selected.name : selectedMilestoneDefault;
173

174 175 176 177
          if (
            $dropdown.hasClass('js-filter-bulk-update') ||
            $dropdown.hasClass('js-issuable-form-dropdown')
          ) {
178 179 180
            e.preventDefault();
            return;
          }
181

182
          if ($dropdown.closest('.add-issues-modal').length) {
183
            modalStoreFilter = ModalStore.store.filter;
184
          }
185

186 187
          if (modalStoreFilter) {
            modalStoreFilter[$dropdown.data('fieldName')] = selected.name;
188 189 190 191 192 193 194
            e.preventDefault();
          } else if ($dropdown.hasClass('js-filter-submit') && (isIssueIndex || isMRIndex)) {
            return Issuable.filterResults($dropdown.closest('form'));
          } else if ($dropdown.hasClass('js-filter-submit')) {
            return $dropdown.closest('form').submit();
          } else if ($dropdown.hasClass('js-issue-board-sidebar')) {
            if (selected.id !== -1 && isSelecting) {
195
              boardStoreIssueSet(
196 197 198 199 200 201
                'milestone',
                new ListMilestone({
                  id: selected.id,
                  title: selected.name,
                }),
              );
Fatih Acet's avatar
Fatih Acet committed
202
            } else {
203
              boardStoreIssueDelete('milestone');
204 205 206 207 208
            }

            $dropdown.trigger('loading.gl.dropdown');
            $loading.removeClass('hidden').fadeIn();

209
            boardsStore.detail.issue
210
              .update($dropdown.attr('data-issue-update'))
211
              .then(() => {
Fatih Acet's avatar
Fatih Acet committed
212 213
                $dropdown.trigger('loaded.gl.dropdown');
                $loading.fadeOut();
214 215 216
              })
              .catch(() => {
                $loading.fadeOut();
Fatih Acet's avatar
Fatih Acet committed
217
              });
218 219 220 221 222 223 224
          } else {
            selected = $selectBox.find('input[type="hidden"]').val();
            data = {};
            data[abilityName] = {};
            data[abilityName].milestone_id = selected != null ? selected : null;
            $loading.removeClass('hidden').fadeIn();
            $dropdown.trigger('loading.gl.dropdown');
225 226
            return axios
              .put(issueUpdateURL, data)
227 228 229 230 231 232 233 234 235
              .then(({ data }) => {
                $dropdown.trigger('loaded.gl.dropdown');
                $loading.fadeOut();
                $selectBox.hide();
                $value.css('display', '');
                if (data.milestone != null) {
                  data.milestone.remaining = timeFor(data.milestone.due_date);
                  data.milestone.name = data.milestone.title;
                  $value.html(milestoneLinkTemplate(data.milestone));
236
                  return $sidebarCollapsedValue
237 238 239 240
                    .attr(
                      'data-original-title',
                      `${data.milestone.name}<br />${data.milestone.remaining}`,
                    )
241 242
                    .find('span')
                    .text(data.milestone.title);
243 244
                } else {
                  $value.html(milestoneLinkNoneTemplate);
245 246 247 248
                  return $sidebarCollapsedValue
                    .attr('data-original-title', __('Milestone'))
                    .find('span')
                    .text(__('None'));
249
                }
250 251 252
              })
              .catch(() => {
                $loading.fadeOut();
253
              });
Fatih Acet's avatar
Fatih Acet committed
254
          }
255
        },
Fatih Acet's avatar
Fatih Acet committed
256
      });
257 258 259
    });
  }
}
260 261

window.MilestoneSelect = MilestoneSelect;