Skip to content
Snippets Groups Projects
Commit 9f976548 authored by Heinrich Lee Yu's avatar Heinrich Lee Yu :basketball: Committed by Heinrich Lee Yu
Browse files

Refactor sidebar to use data from serializer

parent 6c79e930
No related branches found
No related tags found
No related merge requests found
Showing
with 336 additions and 193 deletions
Loading
Loading
@@ -79,11 +79,12 @@ Sidebar.prototype.sidebarToggleClicked = function(e, triggered) {
Sidebar.prototype.toggleTodo = function(e) {
var $btnText, $this, $todoLoading, ajaxType, url;
$this = $(e.currentTarget);
ajaxType = $this.attr('data-delete-path') ? 'delete' : 'post';
if ($this.attr('data-delete-path')) {
url = '' + $this.attr('data-delete-path');
ajaxType = $this.data('deletePath') ? 'delete' : 'post';
if ($this.data('deletePath')) {
url = '' + $this.data('deletePath');
} else {
url = '' + $this.data('url');
url = '' + $this.data('createPath');
}
 
$this.tooltip('hide');
Loading
Loading
Loading
Loading
@@ -5,7 +5,6 @@ module IssuableActions
include Gitlab::Utils::StrongMemoize
 
included do
before_action :labels, only: [:show, :new, :edit]
before_action :authorize_destroy_issuable!, only: :destroy
before_action :authorize_admin_issuable!, only: :bulk_update
end
Loading
Loading
@@ -25,7 +24,10 @@ module IssuableActions
 
def show
respond_to do |format|
format.html
format.html do
@issuable_sidebar = serializer.represent(issuable, serializer: 'sidebar') # rubocop:disable Gitlab/ModuleWithInstanceVariables
end
format.json do
render json: serializer.represent(issuable, serializer: params[:serializer])
end
Loading
Loading
@@ -168,10 +170,6 @@ module IssuableActions
end
end
 
def labels
@labels ||= LabelsFinder.new(current_user, project_id: @project.id).execute # rubocop:disable Gitlab/ModuleWithInstanceVariables
end
def authorize_destroy_issuable!
unless can?(current_user, :"destroy_#{issuable.to_ability_name}", issuable)
return access_denied!
Loading
Loading
Loading
Loading
@@ -8,7 +8,7 @@ class Projects::MergeRequests::ConflictsController < Projects::MergeRequests::Ap
def show
respond_to do |format|
format.html do
labels
@issuable_sidebar = serializer.represent(@merge_request, serializer: 'sidebar')
end
 
format.json do
Loading
Loading
@@ -60,9 +60,15 @@ class Projects::MergeRequests::ConflictsController < Projects::MergeRequests::Ap
end
end
 
private
def authorize_can_resolve_conflicts!
@conflicts_list = ::MergeRequests::Conflicts::ListService.new(@merge_request)
 
return render_404 unless @conflicts_list.can_be_resolved_by?(current_user)
end
def serializer
MergeRequestSerializer.new(current_user: current_user, project: merge_request.project)
end
end
Loading
Loading
@@ -22,8 +22,7 @@ class Projects::MergeRequestsController < Projects::MergeRequests::ApplicationCo
format.html
format.json do
render json: {
html: view_to_html_string("projects/merge_requests/_merge_requests"),
labels: @labels.as_json(methods: :text_color)
html: view_to_html_string("projects/merge_requests/_merge_requests")
}
end
end
Loading
Loading
@@ -43,8 +42,7 @@ class Projects::MergeRequestsController < Projects::MergeRequests::ApplicationCo
 
@noteable = @merge_request
@commits_count = @merge_request.commits_count
labels
@issuable_sidebar = serializer.represent(@merge_request, serializer: 'sidebar')
 
set_pipeline_variables
 
Loading
Loading
Loading
Loading
@@ -23,23 +23,36 @@ module IssuablesHelper
end
end
 
def sidebar_due_date_tooltip_label(issuable)
if issuable.due_date
"#{_('Due date')}<br />#{due_date_remaining_days(issuable)}"
def sidebar_milestone_tooltip_label(milestone)
if milestone && milestone[:due_date]
"#{milestone[:title]}<br/>#{sidebar_milestone_remaining_days(milestone)}"
else
_('Due date')
_('Milestone') + (milestone ? "<br/>#{milestone[:title]}" : "")
end
end
 
def due_date_remaining_days(issuable)
remaining_days_in_words = remaining_days_in_words(issuable)
def sidebar_milestone_remaining_days(milestone)
due_date_remaining_days(due_date: milestone[:due_date], start_date: milestone[:start_date]) if milestone[:due_date]
end
def sidebar_due_date_tooltip_label(due_date)
_('Due date') + (due_date ? "<br/>#{due_date_remaining_days(due_date)}" : "")
end
def due_date_remaining_days(due_date, start_date = nil)
"#{due_date.to_s(:medium)} (#{remaining_days_in_words(due_date: due_date, start_date: start_date)})"
end
def sidebar_label_filter_path(base_path, label_name)
query_params = {label_name: [label_name]}.to_query
 
"#{issuable.due_date.to_s(:medium)} (#{remaining_days_in_words})"
"#{base_path}?#{query_params}"
end
 
def multi_label_name(current_labels, default_label)
if current_labels && current_labels.any?
title = current_labels.first.try(:title)
title = current_labels.first.try(:title) || current_labels.first[:title]
if current_labels.size > 1
"#{title} +#{current_labels.size - 1} more"
else
Loading
Loading
@@ -50,13 +63,13 @@ module IssuablesHelper
end
end
 
def issuable_json_path(issuable, url_params = {})
def issuable_json_path(issuable)
project = issuable.project
 
if issuable.is_a?(MergeRequest)
project_merge_request_path(project, issuable.iid, :json, url_params)
project_merge_request_path(project, issuable.iid, :json)
else
project_issue_path(project, issuable.iid, :json, url_params)
project_issue_path(project, issuable.iid, :json)
end
end
 
Loading
Loading
@@ -197,19 +210,11 @@ module IssuablesHelper
output.join.html_safe
end
 
# rubocop: disable CodeReuse/ActiveRecord
def issuable_todo(issuable)
if current_user
current_user.todos.find_by(target: issuable, state: :pending)
end
end
# rubocop: enable CodeReuse/ActiveRecord
def issuable_labels_tooltip(labels, limit: 5)
first, last = labels.partition.with_index { |_, i| i < limit }
 
if labels && labels.any?
label_names = first.collect(&:name)
label_names = first.collect { |l| l[:title] }
label_names << "and #{last.size} more" unless last.empty?
 
label_names.join(', ')
Loading
Loading
@@ -356,12 +361,6 @@ module IssuablesHelper
issuable.model_name.human.downcase
end
 
def selected_labels
Array(params[:label_name]).map do |label_name|
Label.new(title: label_name)
end
end
def has_filter_bar_param?
finder.class.scalar_params.any? { |p| params[p].present? }
end
Loading
Loading
@@ -386,22 +385,6 @@ module IssuablesHelper
params[:issuable_template] if issuable_templates(issuable).any? { |template| template[:name] == params[:issuable_template] }
end
 
def issuable_todo_button_data(issuable, todo, is_collapsed)
{
todo_text: "Add todo",
mark_text: "Mark todo as done",
todo_icon: (is_collapsed ? sprite_icon('todo-add') : nil),
mark_icon: (is_collapsed ? sprite_icon('todo-done', css_class: 'todo-undone') : nil),
issuable_id: issuable.id,
issuable_type: issuable.class.name.underscore,
url: project_todos_path(@project),
delete_path: (dashboard_todo_path(todo) if todo),
placement: (is_collapsed ? 'left' : nil),
container: (is_collapsed ? 'body' : nil),
boundary: 'viewport'
}
end
def close_reopen_params(issuable, action)
{
issuable.model_name.to_s.underscore => { state_event: action }
Loading
Loading
@@ -418,27 +401,20 @@ module IssuablesHelper
end
end
 
def issuable_sidebar_options(issuable, can_edit_issuable)
def issuable_sidebar_options(sidebar_data)
{
endpoint: issuable_json_path(issuable, serializer: 'sidebar_extras'),
toggleSubscriptionEndpoint: toggle_subscription_path(issuable),
moveIssueEndpoint: move_namespace_project_issue_path(namespace_id: issuable.project.namespace.to_param, project_id: issuable.project, id: issuable),
projectsAutocompleteEndpoint: autocomplete_projects_path(project_id: @project.id),
editable: can_edit_issuable,
currentUser: UserSerializer.new.represent(current_user),
endpoint: "#{sidebar_data[:issuable_json_path]}?serializer=sidebar_extras",
toggleSubscriptionEndpoint: sidebar_data[:toggle_subscription_path],
moveIssueEndpoint: sidebar_data[:move_issue_path],
projectsAutocompleteEndpoint: sidebar_data[:projects_autocomplete_path],
editable: sidebar_data[:can_edit],
currentUser: sidebar_data[:current_user],
rootPath: root_path,
fullPath: @project.full_path
fullPath: sidebar_data[:project_full_path]
}
end
 
def parent
@project || @group
end
def issuable_milestone_tooltip_title(issuable)
if issuable.milestone
milestone_tooltip = milestone_tooltip_title(issuable.milestone)
_('Milestone') + (milestone_tooltip ? ': ' + milestone_tooltip : '')
end
end
end
Loading
Loading
@@ -114,12 +114,6 @@ module MilestonesHelper
end
end
 
def milestone_tooltip_title(milestone)
if milestone
"#{milestone.title}<br />#{milestone_tooltip_due_date(milestone)}"
end
end
def milestone_time_for(date, date_type)
title = date_type == :start ? "Start date" : "End date"
 
Loading
Loading
Loading
Loading
@@ -1422,6 +1422,10 @@ class User < ActiveRecord::Base
todos.where(id: ids)
end
 
def pending_todo_for(target)
todos.find_by(target: target, state: :pending)
end
# @deprecated
alias_method :owned_or_masters_groups, :owned_or_maintainers_groups
 
Loading
Loading
Loading
Loading
@@ -45,13 +45,16 @@ module EntityDateHelper
# If due date is provided, it returns "# days|weeks|months remaining|ago"
# If start date is provided and elapsed, with no due date, it returns "# days elapsed"
def remaining_days_in_words(entity)
if entity.try(:expired?)
start_date = entity.try(:start_date) || entity.try(:[], :start_date)
due_date = entity.try(:due_date) || entity.try(:[], :due_date)
if due_date && due_date.past?
content_tag(:strong, 'Past due')
elsif entity.try(:upcoming?)
elsif start_date && start_date.future?
content_tag(:strong, 'Upcoming')
elsif entity.due_date
is_upcoming = (entity.due_date - Date.today).to_i > 0
time_ago = time_ago_in_words(entity.due_date)
elsif due_date
is_upcoming = (due_date - Date.today).to_i > 0
time_ago = time_ago_in_words(due_date)
 
# https://gitlab.com/gitlab-org/gitlab-ce/issues/49440
#
Loading
Loading
@@ -63,8 +66,8 @@ module EntityDateHelper
remaining_or_ago = is_upcoming ? _("remaining") : _("ago")
 
"#{content} #{remaining_or_ago}".html_safe
elsif entity.start_date && entity.start_date.past?
days = entity.elapsed_days
elsif start_date && start_date.past?
days = (Date.today - start_date).to_i
"#{content_tag(:strong, days)} #{'day'.pluralize(days)} elapsed".html_safe
end
end
Loading
Loading
Loading
Loading
@@ -3,6 +3,109 @@
class IssuableSidebarEntity < Grape::Entity
include RequestAwareEntity
 
with_options if: { include_basic: true } do
expose :id
expose :type do |issuable|
issuable.to_ability_name
end
expose :author_id
expose :project_id do |issuable|
issuable.project.id
end
expose :discussion_locked?, as: :discussion_locked
expose :reference do |issuable|
issuable.to_reference(issuable.project, full: true)
end
expose :current_user, using: UserEntity do |issuable|
request.current_user
end
# Relationships
expose :todo, using: IssuableSidebarTodoEntity do |issuable|
request.current_user.pending_todo_for(issuable) if request.current_user
end
expose :milestone, using: ::API::Entities::Milestone
expose :labels, using: LabelEntity
# Permissions
expose :signed_in do |issuable|
request.current_user.present?
end
expose :can_edit do |issuable|
can?(request.current_user, :"admin_#{issuable.to_ability_name}", issuable.project)
end
expose :can_move do |issuable|
request.current_user && issuable.can_move?(request.current_user)
end
expose :can_admin_label do |issuable|
can?(request.current_user, :admin_label, issuable.project)
end
# Paths
expose :issuable_json_path do |issuable|
if issuable.is_a?(MergeRequest)
project_merge_request_path(issuable.project, issuable.iid, :json)
else
project_issue_path(issuable.project, issuable.iid, :json)
end
end
expose :namespace_path do |issuable|
issuable.project.namespace.full_path
end
expose :project_path do |issuable|
issuable.project.path
end
expose :project_full_path do |issuable|
issuable.project.full_path
end
expose :project_issuables_path do |issuable|
project = issuable.project
namespace = project.namespace
if issuable.is_a?(MergeRequest)
namespace_project_merge_requests_path(namespace, project)
else
namespace_project_issues_path(namespace, project)
end
end
expose :create_todo_path do |issuable|
project_todos_path(issuable.project)
end
expose :project_milestones_path do |issuable|
project_milestones_path(issuable.project, :json)
end
expose :project_labels_path do |issuable|
project_labels_path(issuable.project, :json, include_ancestor_groups: true)
end
expose :toggle_subscription_path do |issuable|
toggle_subscription_path(issuable)
end
expose :move_issue_path do |issuable|
move_namespace_project_issue_path(
namespace_id: issuable.project.namespace.to_param,
project_id: issuable.project,
id: issuable
)
end
expose :projects_autocomplete_path do |issuable|
autocomplete_projects_path(project_id: issuable.project.id)
end
end
with_options if: { include_extras: true } do
include TimeTrackableEntity
 
Loading
Loading
# frozen_string_literal: true
class IssuableSidebarTodoEntity < Grape::Entity
include Gitlab::Routing
expose :id
expose :delete_path do |todo|
dashboard_todo_path(todo) if todo
end
end
Loading
Loading
@@ -7,9 +7,9 @@ class IssueSerializer < BaseSerializer
def represent(issue, opts = {})
entity =
case opts[:serializer]
when 'sidebar_extras'
opts[:include_basic] = false
opts[:include_extras] = true
when 'sidebar', 'sidebar_extras'
opts[:include_basic] = (opts[:serializer] == 'sidebar')
opts[:include_extras] = (opts[:serializer] == 'sidebar_extras')
IssueSidebarEntity
when 'board'
IssueBoardEntity
Loading
Loading
# frozen_string_literal: true
 
class IssueSidebarEntity < IssuableSidebarEntity
with_options if: { include_basic: true } do
expose :due_date
expose :confidential
end
with_options if: { include_extras: true } do
expose :assignees, using: API::Entities::UserBasic
end
Loading
Loading
Loading
Loading
@@ -7,9 +7,9 @@ class MergeRequestSerializer < BaseSerializer
def represent(merge_request, opts = {})
entity =
case opts[:serializer]
when 'sidebar_extras'
opts[:include_basic] = false
opts[:include_extras] = true
when 'sidebar', 'sidebar_extras'
opts[:include_basic] = (opts[:serializer] == 'sidebar')
opts[:include_extras] = (opts[:serializer] == 'sidebar_extras')
MergeRequestSidebarEntity
when 'basic'
MergeRequestBasicEntity
Loading
Loading
# frozen_string_literal: true
 
class MergeRequestSidebarEntity < IssuableSidebarEntity
with_options if: { include_basic: true } do
expose :assignee, using: API::Entities::UserBasic
expose :can_merge do |issuable|
issuable.can_be_merged_by?(issuable.assignee) if issuable.assignee
end
end
end
Loading
Loading
@@ -88,4 +88,4 @@
%section.issuable-discussion
= render 'projects/issues/discussion'
 
= render 'shared/issuable/sidebar', issuable: @issue
= render 'shared/issuable/sidebar', issuable: @issue, issuable_sidebar: @issuable_sidebar
Loading
Loading
@@ -6,7 +6,7 @@
.merge-request-details.issuable-details
= render "projects/merge_requests/mr_box"
 
= render 'shared/issuable/sidebar', issuable: @merge_request
= render 'shared/issuable/sidebar', issuable: @merge_request, issuable_sidebar: @issuable_sidebar
 
#conflicts{ "v-cloak" => "true", data: { conflicts_path: conflicts_project_merge_request_path(@merge_request.project, @merge_request, format: :json),
resolve_conflicts_path: resolve_conflicts_project_merge_request_path(@merge_request.project, @merge_request) } }
Loading
Loading
Loading
Loading
@@ -86,7 +86,8 @@
.mr-loading-status
= spinner
 
= render 'shared/issuable/sidebar', issuable: @merge_request
= render 'shared/issuable/sidebar', issuable: @merge_request, issuable_sidebar: @issuable_sidebar
- if @merge_request.can_be_reverted?(current_user)
= render "projects/commit/change", type: 'revert', commit: @merge_request.merge_commit, title: @merge_request.title
- if @merge_request.can_be_cherry_picked?
Loading
Loading
- todo = issuable_todo(issuable)
- issuable_type = issuable_sidebar[:type]
- signed_in = issuable_sidebar[:signed_in]
- can_edit_issuable = issuable_sidebar[:can_edit]
 
%aside.right-sidebar.js-right-sidebar.js-issuable-sidebar{ data: { signed: { in: current_user.present? } }, class: sidebar_gutter_collapsed_class, 'aria-live' => 'polite' }
.issuable-sidebar{ data: { endpoint: "#{issuable_json_path(issuable)}" } }
- can_edit_issuable = can?(current_user, :"admin_#{issuable.to_ability_name}", @project)
%aside.right-sidebar.js-right-sidebar.js-issuable-sidebar{ data: { signed: { in: signed_in } }, class: sidebar_gutter_collapsed_class, 'aria-live' => 'polite' }
.issuable-sidebar
.block.issuable-sidebar-header
- if current_user
- if signed_in
%span.issuable-header-text.hide-collapsed.float-left
= _('Todo')
%a.gutter-toggle.float-right.js-sidebar-toggle.has-tooltip{ role: "button", href: "#", "aria-label" => "Toggle sidebar", title: sidebar_gutter_tooltip_text, data: { container: 'body', placement: 'left', boundary: 'viewport' } }
= sidebar_gutter_toggle_icon
- if current_user
= render "shared/issuable/sidebar_todo", todo: todo, issuable: issuable
- if signed_in
= render "shared/issuable/sidebar_todo", issuable_sidebar: issuable_sidebar
 
= form_for [@project.namespace.becomes(Namespace), @project, issuable], remote: true, format: :json, html: { class: 'issuable-context-form inline-update js-issuable-update' } do |f|
- if current_user
= form_for issuable_type, url: issuable_sidebar[:issuable_json_path], remote: true, html: { class: 'issuable-context-form inline-update js-issuable-update' } do |f|
- if signed_in
.block.todo.hide-expanded
= render "shared/issuable/sidebar_todo", todo: todo, issuable: issuable, is_collapsed: true
= render "shared/issuable/sidebar_todo", issuable_sidebar: issuable_sidebar, is_collapsed: true
.block.assignee.qa-assignee-block
= render "shared/issuable/sidebar_assignees", issuable: issuable, can_edit_issuable: can_edit_issuable, signed_in: current_user.present?
= render "shared/issuable/sidebar_assignees", issuable: issuable, issuable_sidebar: issuable_sidebar
 
= render_if_exists 'shared/issuable/sidebar_item_epic', issuable: issuable
= render_if_exists 'shared/issuable/sidebar_item_epic', issuable_sidebar: issuable_sidebar
 
- milestone = issuable_sidebar[:milestone] || {}
.block.milestone
.sidebar-collapsed-icon.has-tooltip{ title: milestone_tooltip_title(issuable.milestone), data: { container: 'body', html: 'true', placement: 'left', boundary: 'viewport' } }
.sidebar-collapsed-icon.has-tooltip{ title: sidebar_milestone_tooltip_label(milestone), data: { container: 'body', html: 'true', placement: 'left', boundary: 'viewport' } }
= icon('clock-o', 'aria-hidden': 'true')
%span.milestone-title.collapse-truncated-title
- if issuable.milestone
= issuable.milestone.title
- if milestone.present?
= milestone[:title]
- else
= _('None')
.title.hide-collapsed
Loading
Loading
@@ -35,49 +37,50 @@
- if can_edit_issuable
= link_to _('Edit'), '#', class: 'js-sidebar-dropdown-toggle edit-link float-right'
.value.hide-collapsed
- if issuable.milestone
= link_to issuable.milestone.title, milestone_path(issuable.milestone), class: "bold has-tooltip", title: milestone_tooltip_due_date(issuable.milestone), data: { container: "body", html: 'true', boundary: 'viewport' }
- if milestone.present?
= link_to milestone[:title], milestone[:web_url], class: "bold has-tooltip", title: sidebar_milestone_remaining_days(milestone), data: { container: "body", html: 'true', boundary: 'viewport' }
- else
%span.no-value
= _('None')
 
.selectbox.hide-collapsed
= f.hidden_field 'milestone_id', value: issuable.milestone_id, id: nil
= dropdown_tag('Milestone', options: { title: _('Assign milestone'), toggle_class: 'js-milestone-select js-extra-options', filter: true, dropdown_class: 'dropdown-menu-selectable', placeholder: _('Search milestones'), data: { show_no: true, field_name: "#{issuable.to_ability_name}[milestone_id]", project_id: @project.id, issuable_id: issuable.id, milestones: project_milestones_path(@project, :json), ability_name: issuable.to_ability_name, issue_update: issuable_json_path(issuable), use_id: true, default_no: true, selected: (issuable.milestone.name if issuable.milestone), null_default: true, display: 'static' }})
- if issuable.has_attribute?(:time_estimate)
#issuable-time-tracker.block
// Fallback while content is loading
.title.hide-collapsed
= _('Time tracking')
= icon('spinner spin', 'aria-hidden': 'true')
- if issuable.has_attribute?(:due_date)
= f.hidden_field 'milestone_id', value: milestone[:id], id: nil
= dropdown_tag('Milestone', options: { title: _('Assign milestone'), toggle_class: 'js-milestone-select js-extra-options', filter: true, dropdown_class: 'dropdown-menu-selectable', placeholder: _('Search milestones'), data: { show_no: true, field_name: "#{issuable_type}[milestone_id]", project_id: issuable_sidebar[:project_id], issuable_id: issuable_sidebar[:id], milestones: issuable_sidebar[:project_milestones_path], ability_name: issuable_type, issue_update: issuable_sidebar[:issuable_json_path], use_id: true, default_no: true, selected: milestone[:title], null_default: true, display: 'static' }})
#issuable-time-tracker.block
// Fallback while content is loading
.title.hide-collapsed
= _('Time tracking')
= icon('spinner spin', 'aria-hidden': 'true')
- if issuable_sidebar.has_key?(:due_date)
.block.due_date
.sidebar-collapsed-icon.has-tooltip{ data: { placement: 'left', container: 'body', html: 'true', boundary: 'viewport' }, title: sidebar_due_date_tooltip_label(issuable) }
.sidebar-collapsed-icon.has-tooltip{ data: { placement: 'left', container: 'body', html: 'true', boundary: 'viewport' }, title: sidebar_due_date_tooltip_label(issuable_sidebar[:due_date]) }
= icon('calendar', 'aria-hidden': 'true')
%span.js-due-date-sidebar-value
= issuable.due_date.try(:to_s, :medium) || 'None'
= issuable_sidebar[:due_date].try(:to_s, :medium) || 'None'
.title.hide-collapsed
= _('Due date')
= icon('spinner spin', class: 'hidden block-loading', 'aria-hidden': 'true')
- if can?(current_user, :"admin_#{issuable.to_ability_name}", @project)
- if can_edit_issuable
= link_to _('Edit'), '#', class: 'js-sidebar-dropdown-toggle edit-link float-right'
.value.hide-collapsed
%span.value-content
- if issuable.due_date
%span.bold= issuable.due_date.to_s(:medium)
- if issuable_sidebar[:due_date]
%span.bold= issuable_sidebar[:due_date].to_s(:medium)
- else
%span.no-value
= _('No due date')
- if can?(current_user, :"admin_#{issuable.to_ability_name}", @project)
%span.no-value.js-remove-due-date-holder{ class: ("hidden" if issuable.due_date.nil?) }
- if can_edit_issuable
%span.no-value.js-remove-due-date-holder{ class: ("hidden" if issuable_sidebar[:due_date].nil?) }
\-
%a.js-remove-due-date{ href: "#", role: "button" }
= _('remove due date')
- if can?(current_user, :"admin_#{issuable.to_ability_name}", @project)
- if can_edit_issuable
.selectbox.hide-collapsed
= f.hidden_field :due_date, value: issuable.due_date.try(:strftime, 'yy-mm-dd')
= f.hidden_field :due_date, value: issuable_sidebar[:due_date].try(:strftime, 'yy-mm-dd')
.dropdown
%button.dropdown-menu-toggle.js-due-date-select{ type: 'button', data: { toggle: 'dropdown', field_name: "#{issuable.to_ability_name}[due_date]", ability_name: issuable.to_ability_name, issue_update: issuable_json_path(issuable), display: 'static' } }
%button.dropdown-menu-toggle.js-due-date-select{ type: 'button', data: { toggle: 'dropdown', field_name: "#{issuable_type}[due_date]", ability_name: issuable_type, issue_update: issuable_sidebar[:issuable_json_path], display: 'static' } }
%span.dropdown-toggle-text
= _('Due date')
= icon('chevron-down', 'aria-hidden': 'true')
Loading
Loading
@@ -86,56 +89,56 @@
= dropdown_content do
.js-due-date-calendar
 
- if @labels
- selected_labels = issuable.labels
.block.labels
.sidebar-collapsed-icon.js-sidebar-labels-tooltip{ title: issuable_labels_tooltip(issuable.labels_array), data: { placement: "left", container: "body", boundary: 'viewport' } }
= icon('tags', 'aria-hidden': 'true')
%span
= selected_labels.size
.title.hide-collapsed
= _('Labels')
= icon('spinner spin', class: 'hidden block-loading', 'aria-hidden': 'true')
- if can_edit_issuable
= link_to _('Edit'), '#', class: 'js-sidebar-dropdown-toggle edit-link float-right'
.value.issuable-show-labels.dont-hide.hide-collapsed.qa-labels-block{ class: ("has-labels" if selected_labels.any?) }
- if selected_labels.any?
- selected_labels.each do |label|
= link_to_label(label, subject: issuable.project, type: issuable.to_ability_name)
- else
%span.no-value
= _('None')
.selectbox.hide-collapsed
- selected_labels = issuable_sidebar[:labels]
.block.labels
.sidebar-collapsed-icon.js-sidebar-labels-tooltip{ title: issuable_labels_tooltip(selected_labels), data: { placement: "left", container: "body", boundary: 'viewport' } }
= icon('tags', 'aria-hidden': 'true')
%span
= selected_labels.size
.title.hide-collapsed
= _('Labels')
= icon('spinner spin', class: 'hidden block-loading', 'aria-hidden': 'true')
- if can_edit_issuable
= link_to _('Edit'), '#', class: 'js-sidebar-dropdown-toggle edit-link float-right'
.value.issuable-show-labels.dont-hide.hide-collapsed.qa-labels-block{ class: ("has-labels" if selected_labels.any?) }
- if selected_labels.any?
- selected_labels.each do |label|
= hidden_field_tag "#{issuable.to_ability_name}[label_names][]", label.id, id: nil
.dropdown
%button.dropdown-menu-toggle.js-label-select.js-multiselect.js-label-sidebar-dropdown{ type: "button", data: {toggle: "dropdown", default_label: "Labels", field_name: "#{issuable.to_ability_name}[label_names][]", ability_name: issuable.to_ability_name, show_no: "true", show_any: "true", namespace_path: @project.try(:namespace).try(:full_path), project_path: @project.try(:path), issue_update: issuable_json_path(issuable), labels: (labels_filter_path_with_defaults if @project), display: 'static' } }
%span.dropdown-toggle-text{ class: ("is-default" if selected_labels.empty?) }
= multi_label_name(selected_labels, "Labels")
= icon('chevron-down', 'aria-hidden': 'true')
.dropdown-menu.dropdown-select.dropdown-menu-paging.dropdown-menu-labels.dropdown-menu-selectable
= render partial: "shared/issuable/label_page_default"
- if can? current_user, :admin_label, @project and @project
= render partial: "shared/issuable/label_page_create"
= render_if_exists 'shared/issuable/sidebar_weight', issuable: issuable
- if issuable.has_attribute?(:confidential)
= link_to sidebar_label_filter_path(issuable_sidebar[:project_issuables_path], label[:title]) do
%span.badge.color-label.has-tooltip{ style: "background-color: #{label[:color]}; color: #{label[:text_color]}", title: label[:description], data: { container: "body" } }
= label[:title]
- else
%span.no-value
= _('None')
.selectbox.hide-collapsed
- selected_labels.each do |label|
= hidden_field_tag "#{issuable_type}[label_names][]", label[:id], id: nil
.dropdown
%button.dropdown-menu-toggle.js-label-select.js-multiselect.js-label-sidebar-dropdown{ type: "button", data: {toggle: "dropdown", default_label: "Labels", field_name: "#{issuable_type}[label_names][]", ability_name: issuable_type, show_no: "true", show_any: "true", namespace_path: issuable_sidebar[:namespace_path], project_path: issuable_sidebar[:project_path], issue_update: issuable_sidebar[:issuable_json_path], labels: issuable_sidebar[:project_labels_path], display: 'static' } }
%span.dropdown-toggle-text{ class: ("is-default" if selected_labels.empty?) }
= multi_label_name(selected_labels, "Labels")
= icon('chevron-down', 'aria-hidden': 'true')
.dropdown-menu.dropdown-select.dropdown-menu-paging.dropdown-menu-labels.dropdown-menu-selectable
= render partial: "shared/issuable/label_page_default"
- if issuable_sidebar[:can_admin_label]
= render partial: "shared/issuable/label_page_create"
= render_if_exists 'shared/issuable/sidebar_weight', issuable_sidebar: issuable_sidebar
- if issuable_sidebar.has_key?(:confidential)
-# haml-lint:disable InlineJavaScript
%script#js-confidential-issue-data{ type: "application/json" }= { is_confidential: @issue.confidential, is_editable: can_edit_issuable }.to_json.html_safe
%script#js-confidential-issue-data{ type: "application/json" }= { is_confidential: issuable_sidebar[:confidential], is_editable: can_edit_issuable }.to_json.html_safe
#js-confidential-entry-point
 
- if issuable.has_attribute?(:discussion_locked)
-# haml-lint:disable InlineJavaScript
%script#js-lock-issue-data{ type: "application/json" }= { is_locked: issuable.discussion_locked?, is_editable: can_edit_issuable }.to_json.html_safe
#js-lock-entry-point
-# haml-lint:disable InlineJavaScript
%script#js-lock-issue-data{ type: "application/json" }= { is_locked: issuable_sidebar[:discussion_locked], is_editable: can_edit_issuable }.to_json.html_safe
#js-lock-entry-point
 
.js-sidebar-participants-entry-point
 
- if current_user
- if signed_in
.js-sidebar-subscriptions-entry-point
 
- project_ref = cross_project_reference(@project, issuable)
- project_ref = issuable_sidebar[:reference]
.block.project-reference
.sidebar-collapsed-icon.dont-change-state
= clipboard_button(text: project_ref, title: _('Copy reference to clipboard'), placement: "left", boundary: 'viewport')
Loading
Loading
@@ -145,7 +148,8 @@
%cite{ title: project_ref }
= project_ref
= clipboard_button(text: project_ref, title: _('Copy reference to clipboard'), placement: "left", boundary: 'viewport')
- if current_user && issuable.can_move?(current_user)
- if issuable_sidebar[:can_move]
.block.js-sidebar-move-issue-block
.sidebar-collapsed-icon{ data: { toggle: 'tooltip', placement: 'left', container: 'body', boundary: 'viewport' }, title: _('Move issue') }
= custom_icon('icon_arrow_right')
Loading
Loading
@@ -164,4 +168,4 @@
= icon('spinner spin', class: 'sidebar-move-issue-confirmation-loading-icon')
 
-# haml-lint:disable InlineJavaScript
%script.js-sidebar-options{ type: "application/json" }= issuable_sidebar_options(issuable, can_edit_issuable).to_json.html_safe
%script.js-sidebar-options{ type: "application/json" }= issuable_sidebar_options(issuable_sidebar).to_json.html_safe
- if issuable.is_a?(Issue)
#js-vue-sidebar-assignees{ data: { field: "#{issuable.to_ability_name}[assignee_ids]", signed_in: signed_in } }
- issuable_type = issuable_sidebar[:type]
- signed_in = issuable_sidebar[:signed_in]
- can_edit_issuable = issuable_sidebar[:can_edit]
- if issuable_type == "issue"
#js-vue-sidebar-assignees{ data: { field: "#{issuable_type}[assignee_ids]", signed_in: signed_in } }
.title.hide-collapsed
= _('Assignee')
= icon('spinner spin')
- else
.sidebar-collapsed-icon.sidebar-collapsed-user{ data: { toggle: "tooltip", placement: "left", container: "body", boundary: 'viewport' }, title: sidebar_assignee_tooltip_label(issuable) }
- if issuable.assignee
.sidebar-collapsed-icon.sidebar-collapsed-user{ data: { toggle: "tooltip", placement: "left", container: "body", boundary: 'viewport' }, title: (issuable_sidebar.dig(:assignee, :name) || _('Assignee')) }
- if issuable_sidebar[:assignee]
= link_to_member(@project, issuable.assignee, size: 24)
- else
= icon('user', 'aria-hidden': 'true')
Loading
Loading
@@ -18,13 +22,13 @@
%a.gutter-toggle.float-right.js-sidebar-toggle{ role: "button", href: "#", "aria-label" => _('Toggle sidebar') }
= sidebar_gutter_toggle_icon
.value.hide-collapsed
- if issuable.assignee
- if issuable_sidebar[:assignee]
= link_to_member(@project, issuable.assignee, size: 32, extra_class: 'bold') do
- if !issuable.can_be_merged_by?(issuable.assignee)
- if issuable_sidebar[:can_merge]
%span.float-right.cannot-be-merged{ data: { toggle: 'tooltip', placement: 'left' }, title: _('Not allowed to merge') }
= icon('exclamation-triangle', 'aria-hidden': 'true')
%span.username
= issuable.assignee.to_reference
@#{issuable_sidebar[:assignee][:username]}
- else
%span.assign-yourself.no-value
= _('No assignee')
Loading
Loading
@@ -34,19 +38,33 @@
= _('assign yourself')
 
.selectbox.hide-collapsed
- issuable.assignees.each do |assignee|
= hidden_field_tag "#{issuable.to_ability_name}[assignee_ids][]", assignee.id, id: nil, data: { avatar_url: assignee.avatar_url, name: assignee.name, username: assignee.username }
- if issuable.assignees.none?
= hidden_field_tag "#{issuable_type}[assignee_ids][]", 0, id: nil
- else
- issuable.assignees.each do |assignee|
= hidden_field_tag "#{issuable_type}[assignee_ids][]", assignee.id, id: nil, data: { avatar_url: assignee.avatar_url, name: assignee.name, username: assignee.username }
 
- options = { toggle_class: 'js-user-search js-author-search', title: _('Assign to'), filter: true, dropdown_class: 'dropdown-menu-user dropdown-menu-selectable dropdown-menu-author', placeholder: _('Search users'), data: { first_user: current_user&.username, current_user: true, project_id: @project&.id, author_id: issuable.author_id, field_name: "#{issuable.to_ability_name}[assignee_ids][]", issue_update: issuable_json_path(issuable), ability_name: issuable.to_ability_name, null_user: true, display: 'static' } }
- options = { toggle_class: 'js-user-search js-author-search',
title: _('Assign to'),
filter: true,
dropdown_class: 'dropdown-menu-user dropdown-menu-selectable dropdown-menu-author',
placeholder: _('Search users'),
data: { first_user: issuable_sidebar.dig(:current_user, :username),
current_user: true,
project_id: issuable_sidebar[:project_id],
author_id: issuable_sidebar[:author_id],
field_name: "#{issuable_type}[assignee_ids][]",
issue_update: issuable_sidebar[:issuable_json_path],
ability_name: issuable_type,
null_user: true,
display: 'static' } }
- title = _('Select assignee')
 
- if issuable.is_a?(Issue)
- unless issuable.assignees.any?
= hidden_field_tag "#{issuable.to_ability_name}[assignee_ids][]", 0, id: nil
- if issuable_type == "issue"
- dropdown_options = issue_assignees_dropdown_options
- title = dropdown_options[:title]
- options[:toggle_class] += ' js-multiselect js-save-user-data'
- data = { field_name: "#{issuable.to_ability_name}[assignee_ids][]" }
- data = { field_name: "#{issuable_type}[assignee_ids][]" }
- data[:multi_select] = true
- data['dropdown-title'] = title
- data['dropdown-header'] = dropdown_options[:data][:'dropdown-header']
Loading
Loading
- is_collapsed = local_assigns.fetch(:is_collapsed, false)
- mark_content = is_collapsed ? sprite_icon('todo-done', css_class: 'todo-undone') : _('Mark todo as done')
- todo_content = is_collapsed ? sprite_icon('todo-add') : _('Add todo')
- todo = issuable_sidebar[:todo] || {}
- todo_text = _('Add todo')
- mark_text = _('Mark todo as done')
- todo_icon = sprite_icon('todo-add')
- mark_icon = sprite_icon('todo-done', css_class: 'todo-undone')
- mark_content = is_collapsed ? mark_icon : mark_text
- todo_content = is_collapsed ? todo_icon : todo_text
 
%button.issuable-todo-btn.js-issuable-todo{ type: 'button',
class: (is_collapsed ? 'btn-blank sidebar-collapsed-icon dont-change-state has-tooltip' : 'btn btn-default issuable-header-btn float-right'),
title: (todo.nil? ? _('Add todo') : _('Mark todo as done')),
'aria-label' => (todo.nil? ? _('Add todo') : _('Mark todo as done')),
data: issuable_todo_button_data(issuable, todo, is_collapsed) }
title: (todo[:id] ? mark_text : todo_text),
'aria-label' => (todo[:id] ? mark_text : todo_text),
data: { todo_text: todo_text,
mark_text: mark_text,
todo_icon: is_collapsed ? todo_icon : nil,
mark_icon: is_collapsed ? mark_icon : nil,
issuable_id: issuable_sidebar[:id],
issuable_type: issuable_sidebar[:type],
create_path: issuable_sidebar[:create_todo_path],
delete_path: todo[:delete_path] } }
%span.issuable-todo-inner.js-issuable-todo-inner<
- if todo
- if todo[:id]
= mark_content
- else
= todo_content
Loading
Loading
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment