milestones_helper.rb 7.47 KB
Newer Older
1 2
# frozen_string_literal: true

3
module MilestonesHelper
4
  include EntityDateHelper
5
  include Gitlab::Utils::StrongMemoize
6

7 8
  def milestones_filter_path(opts = {})
    if @project
9
      project_milestones_path(@project, opts)
10 11
    elsif @group
      group_milestones_path(@group, opts)
Douwe Maan's avatar
Douwe Maan committed
12 13
    else
      dashboard_milestones_path(opts)
14 15
    end
  end
16

17 18
  def milestones_label_path(opts = {})
    if @project
19
      project_issues_path(@project, opts)
20 21 22 23 24 25 26
    elsif @group
      issues_group_path(@group, opts)
    else
      issues_dashboard_path(opts)
    end
  end

27 28
  def milestones_browse_issuables_path(milestone, state: nil, type:)
    opts = { milestone_title: milestone.title, state: state }
29 30 31 32 33 34 35 36 37 38 39

    if @project
      polymorphic_path([@project.namespace.becomes(Namespace), @project, type], opts)
    elsif @group
      polymorphic_url([type, @group], opts)
    else
      polymorphic_url([type, :dashboard], opts)
    end
  end

  def milestone_issues_by_label_count(milestone, label, state:)
40 41 42 43 44 45 46 47 48 49 50 51
    issues = milestone.issues.with_label(label.title)
    issues =
      case state
      when :opened
        issues.opened
      when :closed
        issues.closed
      else
        raise ArgumentError, "invalid milestone state `#{state}`"
      end

    issues.size
52 53
  end

54
  # Returns count of milestones for different states
55
  # Uses explicit hash keys as the 'opened' state URL params differs from the db value
56
  # and we need to add the total
57
  # rubocop: disable CodeReuse/ActiveRecord
58 59
  def milestone_counts(milestones)
    counts = milestones.reorder(nil).group(:state).count
60

61
    {
62 63 64
      opened: counts['active'] || 0,
      closed: counts['closed'] || 0,
      all: counts.values.sum || 0
65
    }
66
  end
67
  # rubocop: enable CodeReuse/ActiveRecord
68

69 70 71
  # Show 'active' class if provided GET param matches check
  # `or_blank` allows the function to return 'active' when given an empty param
  # Could be refactored to be simpler but that may make it harder to read
72
  def milestone_class_for_state(param, check, match_blank_param = false)
73 74
    if match_blank_param
      'active' if param.blank? || param == check
Felipe Artur's avatar
Felipe Artur committed
75 76
    elsif param == check
      'active'
77
    else
Felipe Artur's avatar
Felipe Artur committed
78
      check
79 80 81
    end
  end

82 83 84 85 86 87 88 89 90 91 92 93 94
  def milestone_progress_tooltip_text(milestone)
    has_issues = milestone.total_issues_count(current_user) > 0

    if has_issues
      [
        _('Progress'),
        _("%{percent}%% complete") % { percent: milestone.percent_complete(current_user) }
      ].join('<br />')
    else
      _('Progress')
    end
  end

95 96
  def milestone_progress_bar(milestone)
    options = {
97
      class: 'progress-bar bg-success',
98
      style: "width: #{milestone.percent_complete(current_user)}%;"
99 100 101 102 103 104
    }

    content_tag :div, class: 'progress' do
      content_tag :div, nil, options
    end
  end
105

106
  def milestones_filter_dropdown_path
107
    project = @target_project || @project
108
    if project
109
      project_milestones_path(project, :json)
110 111
    elsif @group
      group_milestones_path(@group, :json)
112
    else
113
      dashboard_milestones_path(:json)
114
    end
115
  end
116

117
  def milestone_tooltip_title(milestone)
118 119
    if milestone
      "#{milestone.title}<br />#{milestone_tooltip_due_date(milestone)}"
120 121 122
    end
  end

123 124 125 126
  def milestone_time_for(date, date_type)
    title = date_type == :start ? "Start date" : "End date"

    if date
127 128 129 130 131 132
      time_ago = time_ago_in_words(date).sub("about ", "")
      state = if date.past?
                "ago"
              else
                "remaining"
              end
133 134 135 136 137

      content = [
        title,
        "<br />",
        date.to_s(:medium),
138
        "(#{time_ago} #{state})"
139 140
      ].join(" ")

141
      content.html_safe
142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176
    else
      title
    end
  end

  def milestone_issues_tooltip_text(milestone)
    issues = milestone.count_issues_by_state(current_user)

    return _("Issues") if issues.empty?

    content = []

    content << n_("1 open issue", "%d open issues", issues["opened"]) % issues["opened"] if issues["opened"]
    content << n_("1 closed issue", "%d closed issues", issues["closed"]) % issues["closed"] if issues["closed"]

    content.join('<br />').html_safe
  end

  def milestone_merge_requests_tooltip_text(milestone)
    merge_requests = milestone.merge_requests

    return _("Merge requests") if merge_requests.empty?

    content = []

    content << n_("1 open merge request", "%d open merge requests", merge_requests.opened.count) % merge_requests.opened.count if merge_requests.opened.any?
    content << n_("1 closed merge request", "%d closed merge requests", merge_requests.closed.count) % merge_requests.closed.count if merge_requests.closed.any?
    content << n_("1 merged merge request", "%d merged merge requests", merge_requests.merged.count) % merge_requests.merged.count if merge_requests.merged.any?

    content.join('<br />').html_safe
  end

  def milestone_tooltip_due_date(milestone)
    if milestone.due_date
      "#{milestone.due_date.to_s(:medium)} (#{remaining_days_in_words(milestone)})"
177 178
    else
      _('Milestone')
179 180 181 182 183
    end
  end

  def milestone_date_range(milestone)
    if milestone.start_date && milestone.due_date
184
      "#{milestone.start_date.to_s(:medium)}#{milestone.due_date.to_s(:medium)}"
185 186 187 188 189 190 191 192 193 194 195 196
    elsif milestone.due_date
      if milestone.due_date.past?
        "expired on #{milestone.due_date.to_s(:medium)}"
      else
        "expires on #{milestone.due_date.to_s(:medium)}"
      end
    elsif milestone.start_date
      if milestone.start_date.past?
        "started on #{milestone.start_date.to_s(:medium)}"
      else
        "starts on #{milestone.start_date.to_s(:medium)}"
      end
197 198
    end
  end
199

200
  def milestone_merge_request_tab_path(milestone)
201
    if @project
202
      merge_requests_project_milestone_path(@project, milestone, format: :json)
203 204
    elsif @group
      merge_requests_group_milestone_path(@group, milestone.safe_title, title: milestone.title, format: :json)
205 206
    else
      merge_requests_dashboard_milestone_path(milestone, title: milestone.title, format: :json)
207 208 209
    end
  end

210
  def milestone_participants_tab_path(milestone)
211
    if @project
212
      participants_project_milestone_path(@project, milestone, format: :json)
213 214
    elsif @group
      participants_group_milestone_path(@group, milestone.safe_title, title: milestone.title, format: :json)
215 216
    else
      participants_dashboard_milestone_path(milestone, title: milestone.title, format: :json)
217 218 219
    end
  end

220
  def milestone_labels_tab_path(milestone)
221
    if @project
222
      labels_project_milestone_path(@project, milestone, format: :json)
223 224
    elsif @group
      labels_group_milestone_path(@group, milestone.safe_title, title: milestone.title, format: :json)
225 226
    else
      labels_dashboard_milestone_path(milestone, title: milestone.title, format: :json)
227 228
    end
  end
Felipe Artur's avatar
Felipe Artur committed
229 230 231 232

  def group_milestone_route(milestone, params = {})
    params = nil if params.empty?

233
    if milestone.legacy_group_milestone?
Felipe Artur's avatar
Felipe Artur committed
234 235 236 237 238
      group_milestone_path(@group, milestone.safe_title, title: milestone.title, milestone: params)
    else
      group_milestone_path(@group, milestone.iid, milestone: params)
    end
  end
239 240 241 242 243 244 245 246

  def group_or_dashboard_milestone_path(milestone)
    if milestone.group_milestone?
      group_milestone_path(milestone.group, milestone.iid, milestone: { title: milestone.title })
    else
      dashboard_milestone_path(milestone.safe_title, title: milestone.title)
    end
  end
247 248 249 250 251 252 253 254 255 256 257 258

  def can_admin_project_milestones?
    strong_memoize(:can_admin_project_milestones) do
      can?(current_user, :admin_milestone, @project)
    end
  end

  def can_admin_group_milestones?
    strong_memoize(:can_admin_group_milestones) do
      can?(current_user, :admin_milestone, @project.group)
    end
  end
259
end