diff options
Diffstat (limited to 'app')
24 files changed, 447 insertions, 125 deletions
diff --git a/app/assets/javascripts/gl_dropdown.js.coffee b/app/assets/javascripts/gl_dropdown.js.coffee index 4b78bcde774..2b56ab2e6de 100644 --- a/app/assets/javascripts/gl_dropdown.js.coffee +++ b/app/assets/javascripts/gl_dropdown.js.coffee @@ -126,8 +126,10 @@ class GitLabDropdown @selectFirstRow() # Event listeners + @dropdown.on "shown.bs.dropdown", @opened @dropdown.on "hidden.bs.dropdown", @hidden + @dropdown.on "click", ".dropdown-menu, .dropdown-menu-close", @shouldPropagate if @dropdown.find(".dropdown-toggle-page").length @dropdown.find(".dropdown-toggle-page, .dropdown-menu-back").on "click", (e) => @@ -143,10 +145,11 @@ class GitLabDropdown selector = ".dropdown-page-one .dropdown-content a" @dropdown.on "click", selector, (e) -> + e.preventDefault() self.rowClicked $(@) if self.options.clicked - self.options.clicked() + self.options.clicked.call(@,e) toggleLoading: -> $('.dropdown-menu', @dropdown).toggleClass LOADING_CLASS @@ -176,6 +179,15 @@ class GitLabDropdown @appendMenu(full_html) + shouldPropagate: (e) => + if @options.multiSelect + $target = $(e.target) + if not $target.hasClass('dropdown-menu-close') and not $target.hasClass('dropdown-menu-close-icon') + e.stopPropagation() + return false + else + return true + opened: => contentHtml = $('.dropdown-content', @dropdown).html() if @remote && contentHtml is "" @@ -184,7 +196,7 @@ class GitLabDropdown if @options.filterable @dropdown.find(".dropdown-input-field").focus() - hidden: => + hidden: (e) => if @options.filterable @dropdown .find(".dropdown-input-field") @@ -195,6 +207,9 @@ class GitLabDropdown if @dropdown.find(".dropdown-toggle-page").length $('.dropdown-menu', @dropdown).removeClass PAGE_TWO_CLASS + if @options.hidden + @options.hidden.call(@,e) + # Render the full menu renderMenu: (html) -> @@ -226,6 +241,13 @@ class GitLabDropdown html = @options.renderRow(data) else selected = if @options.isSelected then @options.isSelected(data) else false + if not selected + value = if @options.id then @options.id(data) else data.id + fieldName = @options.fieldName + field = @dropdown.parent().find("input[name='#{fieldName}'][value='#{value}']") + if field.length + selected = true + url = if @options.url then @options.url(data) else "#" text = if @options.text then @options.text(data) else "" cssClass = ""; @@ -258,26 +280,28 @@ class GitLabDropdown rowClicked: (el) -> fieldName = @options.fieldName - field = @dropdown.parent().find("input[name='#{fieldName}']") - + selectedIndex = el.parent().index() + if @renderedData + selectedObject = @renderedData[selectedIndex] + value = if @options.id then @options.id(selectedObject, el) else selectedObject.id + field = @dropdown.parent().find("input[name='#{fieldName}'][value='#{value}']") if el.hasClass(ACTIVE_CLASS) + el.removeClass(ACTIVE_CLASS) field.remove() else fieldName = @options.fieldName selectedIndex = el.parent().index() if @renderedData selectedObject = @renderedData[selectedIndex] + selectedObject.selected = true value = if @options.id then @options.id(selectedObject, el) else selectedObject.id if !value? field.remove() - if @options.multiSelect - oldValue = field.val() - if oldValue - value = "#{oldValue},#{value}" - else + if not @options.multiSelect @dropdown.find(".#{ACTIVE_CLASS}").removeClass ACTIVE_CLASS + @dropdown.parent().find("input[name='#{fieldName}']").remove() # Toggle active class for the tick mark el.toggleClass "is-active" @@ -285,15 +309,15 @@ class GitLabDropdown # Toggle the dropdown label if @options.toggleLabel $(@el).find(".dropdown-toggle-text").text @options.toggleLabel(selectedObject) - if value? if !field.length # Create hidden input for form - input = "<input type='hidden' name='#{fieldName}' />" + input = "<input type='hidden' name='#{fieldName}' value='#{value}' />" + if @options.inputId? + input = $(input) + .attr('id', @options.inputId) @dropdown.before input - @dropdown.parent().find("input[name='#{fieldName}']").val value - selectFirstRow: -> selector = '.dropdown-content li:first-child a' if @dropdown.find(".dropdown-toggle-page").length diff --git a/app/assets/javascripts/issuable_context.js.coffee b/app/assets/javascripts/issuable_context.js.coffee index d6d09b36d8d..6fc924d3d66 100644 --- a/app/assets/javascripts/issuable_context.js.coffee +++ b/app/assets/javascripts/issuable_context.js.coffee @@ -1,8 +1,7 @@ class @IssuableContext - constructor: -> + constructor: (currentUser) -> @initParticipants() - - new UsersSelect() + new UsersSelect(currentUser) $('select.select2').select2({width: 'resolve', dropdownAutoWidth: true}) $(".issuable-sidebar .inline-update").on "change", "select", -> @@ -11,10 +10,20 @@ class @IssuableContext $(this).submit() $(document).on "click",".edit-link", (e) -> - block = $(@).parents('.block') - block.find('.selectbox').show() - block.find('.value').hide() - block.find('.js-select2').select2("open") + $block = $(@).parents('.block') + $selectbox = $block.find('.selectbox') + if $selectbox.is(':visible') + $selectbox.hide() + $block.find('.value').show() + else + $selectbox.show() + $block.find('.value').hide() + + if $selectbox.is(':visible') + setTimeout (-> + $block.find('.dropdown-menu-toggle').trigger 'click' + ), 0 + $(".right-sidebar").niceScroll() diff --git a/app/assets/javascripts/labels_select.js.coffee b/app/assets/javascripts/labels_select.js.coffee index e08648d583b..b5c7af9a8ad 100644 --- a/app/assets/javascripts/labels_select.js.coffee +++ b/app/assets/javascripts/labels_select.js.coffee @@ -4,14 +4,20 @@ class @LabelsSelect $dropdown = $(dropdown) projectId = $dropdown.data('project-id') labelUrl = $dropdown.data('labels') + issueUpdateURL = $dropdown.data('issueUpdate') selectedLabel = $dropdown.data('selected') - if selectedLabel - selectedLabel = selectedLabel.toString().split(',') + if selectedLabel? + selectedLabel = selectedLabel.split(',') newLabelField = $('#new_label_name') newColorField = $('#new_label_color') showNo = $dropdown.data('show-no') showAny = $dropdown.data('show-any') defaultLabel = $dropdown.data('default-label') + abilityName = $dropdown.data('ability-name') + $selectbox = $dropdown.closest('.selectbox') + $block = $selectbox.closest('.block') + $value = $block.find('.value') + $loading = $block.find('.block-loading').fadeOut() if newLabelField.length $newLabelCreateButton = $('.js-new-label-btn') @@ -21,6 +27,22 @@ class @LabelsSelect # Suggested colors in the dropdown to chose from pre-chosen colors $('.suggest-colors-dropdown a').on 'click', (e) -> + + issueURLSplit = issueUpdateURL.split('/') if issueUpdateURL? + if issueUpdateURL + labelHTMLTemplate = _.template( + '<% _.each(labels, function(label){ %> + <a href="<%= ["",issueURLSplit[1], issueURLSplit[2],""].join("/") %>issues?label_name=<%= label.title %>"> + <span class="label color-label" style="background-color: <%= label.color %>;"> + <%= label.title %> + </span> + </a> + <% }); %>' + ); + labelNoneHTMLTemplate = _.template('<div class="light">None</div>') + + if newLabelField.length and $dropdown.hasClass 'js-extra-options' + $('.suggest-colors-dropdown a').on "click", (e) -> e.preventDefault() e.stopPropagation() newColorField @@ -57,6 +79,23 @@ class @LabelsSelect # This allows us to enable the button when ready enableLabelCreateButton = -> if newLabelField.val() isnt '' and newColorField.val() isnt '' + $newLabelError.hide() + $('.js-new-label-btn').disable() + + # Create new label with API + Api.newLabel projectId, { + name: newLabelField.val() + color: newColorField.val() + }, (label) -> + $('.js-new-label-btn').enable() + + if label.message? + $newLabelError + .text label.message + .show() + else + $('.dropdown-menu-back', $dropdown.parent()).trigger 'click' + $newLabelCreateButton.enable() else $newLabelCreateButton.disable() @@ -90,41 +129,78 @@ class @LabelsSelect else $('.dropdown-menu-back', $dropdown.parent()).trigger 'click' + saveLabelData = -> + selected = $dropdown + .closest('.selectbox') + .find("input[name='#{$dropdown.data('field-name')}']") + .map(-> + @value + ).get() + data = {} + data[abilityName] = {} + data[abilityName].label_ids = selected + if not selected.length + data[abilityName].label_ids = [''] + $loading.fadeIn() + $.ajax( + type: 'PUT' + url: issueUpdateURL + dataType: 'JSON' + data: data + ).done (data) -> + $loading.fadeOut() + $selectbox.hide() + data.issueURLSplit = issueURLSplit + if not data.labels.length + template = labelNoneHTMLTemplate() + else + template = labelHTMLTemplate(data) + href = $value + .show() + .html(template) + $value + .find('a') + .each((i) -> + setTimeout(=> + glAnimate($(@), 'pulse') + ,200 * i + ) + ) + + $dropdown.glDropdown( data: (term, callback) -> $.ajax( url: labelUrl ).done (data) -> - if showNo - data.unshift( - id: 0 - title: 'No Label' - ) - - if showAny - data.unshift( - isAny: true - title: 'Any Label' - ) - - if data.length > 2 - data.splice 2, 0, 'divider' - + if $dropdown.hasClass 'js-extra-options' + if showNo + data.unshift( + id: 0 + title: 'No Label' + ) + + if showAny + data.unshift( + isAny: true + title: 'Any Label' + ) + + if data.length > 2 + data.splice 2, 0, 'divider' callback data + renderRow: (label) -> - if $.isArray(selectedLabel) - selected = '' - $.each selectedLabel, (i, selectedLbl) -> - selectedLbl = selectedLbl.trim() - if selected is '' and label.title is selectedLbl - selected = 'is-active' - else - selected = if label.title is selectedLabel then 'is-active' else '' + selectedClass = '' + if $selectbox.find("input[type='hidden']\ + [name='#{$dropdown.data('field-name')}']\ + [value='#{label.id}']").length + selectedClass = 'is-active' color = if label.color? then "<span class='dropdown-label-box' style='background-color: #{label.color}'></span>" else "" "<li> - <a href='#' class='#{selected}'> + <a href='#' class='#{selectedClass}'> #{color} #{label.title} </a> @@ -133,6 +209,7 @@ class @LabelsSelect search: fields: ['title'] selectable: true + toggleLabel: (selected) -> if selected and selected.title isnt 'Any Label' selected.title @@ -142,8 +219,19 @@ class @LabelsSelect id: (label) -> if label.isAny? '' - else + else if $dropdown.hasClass "js-filter-submit" label.title + else + label.id + + hidden: -> + $selectbox.hide() + $value.show() + if $dropdown.hasClass 'js-multiselect' + saveLabelData() + + multiSelect: $dropdown.hasClass 'js-multiselect' + clicked: -> page = $('body').data 'page' isIssueIndex = page is 'projects:issues:index' @@ -153,4 +241,9 @@ class @LabelsSelect Issues.filterResults $dropdown.closest('form') else if $dropdown.hasClass 'js-filter-submit' $dropdown.closest('form').submit() + else + if $dropdown.hasClass 'js-multiselect' + return + else + saveLabelData() ) diff --git a/app/assets/javascripts/lib/animate.js.coffee b/app/assets/javascripts/lib/animate.js.coffee new file mode 100644 index 00000000000..8f892b5a2b9 --- /dev/null +++ b/app/assets/javascripts/lib/animate.js.coffee @@ -0,0 +1,13 @@ +((w) -> + + w.glAnimate = ($el, animation, done) -> + $el + .removeClass() + .addClass(animation + ' animated') + .one 'webkitAnimationEnd mozAnimationEnd MSAnimationEnd oanimationend animationend', -> + $(this).removeClass() + return + return + return + +) window
\ No newline at end of file diff --git a/app/assets/javascripts/milestone_select.js.coffee b/app/assets/javascripts/milestone_select.js.coffee index e17a1adb648..d1746c38e74 100644 --- a/app/assets/javascripts/milestone_select.js.coffee +++ b/app/assets/javascripts/milestone_select.js.coffee @@ -1,35 +1,52 @@ class @MilestoneSelect - constructor: -> + constructor: (currentProject) -> + if currentProject? + _this = @ + @currentProject = JSON.parse(currentProject) $('.js-milestone-select').each (i, dropdown) -> $dropdown = $(dropdown) projectId = $dropdown.data('project-id') milestonesUrl = $dropdown.data('milestones') + issueUpdateURL = $dropdown.data('issueUpdate') selectedMilestone = $dropdown.data('selected') showNo = $dropdown.data('show-no') showAny = $dropdown.data('show-any') useId = $dropdown.data('use-id') defaultLabel = $dropdown.data('default-label') + issuableId = $dropdown.data('issuable-id') + abilityName = $dropdown.data('ability-name') + $selectbox = $dropdown.closest('.selectbox') + $block = $selectbox.closest('.block') + $value = $block.find('.value') + $loading = $block.find('.block-loading').fadeOut() + + if issueUpdateURL + milestoneLinkTemplate = _.template( + '<a href="/<%= namespace %>/<%= path %>/milestones/<%= iid %>"><%= title %></a>' + ) + + milestoneLinkNoneTemplate = '<div class="light">None</div>' $dropdown.glDropdown( data: (term, callback) -> $.ajax( url: milestonesUrl ).done (data) -> - if showNo - data.unshift( - id: '0' - title: 'No Milestone' - ) - - if showAny - data.unshift( - isAny: true - title: 'Any Milestone' - ) + if $dropdown.hasClass "js-extra-options" + if showNo + data.unshift( + id: '0' + title: 'No Milestone' + ) - if data.length > 2 - data.splice 2, 0, 'divider' + if showAny + data.unshift( + isAny: true + title: 'Any Milestone' + ) + if data.length > 2 + data.splice 2, 0, 'divider' callback(data) filterable: true search: @@ -53,13 +70,38 @@ class @MilestoneSelect milestone.id isSelected: (milestone) -> milestone.title is selectedMilestone - clicked: -> - page = $('body').data 'page' - isIssueIndex = page is 'projects:issues:index' - isMRIndex = page is page is 'projects:merge_requests:index' - - if $dropdown.hasClass('js-filter-submit') and (isIssueIndex or isMRIndex) - Issues.filterResults $dropdown.closest('form') - else if $dropdown.hasClass 'js-filter-submit' - $dropdown.closest('form').submit() - ) + hidden: -> + $selectbox.hide() + $value.show() + clicked: (e) -> + if $dropdown.hasClass 'js-filter-bulk-update' + return + + if $dropdown.hasClass 'js-filter-submit' + $dropdown.parents('form').submit() + else + selected = $selectbox + .find('input[type="hidden"]') + .val() + data = {} + data[abilityName] = {} + data[abilityName].milestone_id = selected + $loading + .fadeIn() + $.ajax( + type: 'PUT' + url: issueUpdateURL + data: data + ).done (data) -> + $loading.fadeOut() + $selectbox.hide() + $milestoneLink = $value + .show() + .find('a') + if data.milestone? + data.milestone.namespace = _this.currentProject.namespace + data.milestone.path = _this.currentProject.path + $value.html(milestoneLinkTemplate(data.milestone)) + else + $value.html(milestoneLinkNoneTemplate) + )
\ No newline at end of file diff --git a/app/assets/javascripts/users_select.js.coffee b/app/assets/javascripts/users_select.js.coffee index 84193400890..3262d8b8c90 100644 --- a/app/assets/javascripts/users_select.js.coffee +++ b/app/assets/javascripts/users_select.js.coffee @@ -1,7 +1,9 @@ class @UsersSelect - constructor: -> + constructor: (currentUser) -> @usersPath = "/autocomplete/users.json" @userPath = "/autocomplete/users/:id.json" + if currentUser? + @currentUser = JSON.parse(currentUser) $('.js-user-search').each (i, dropdown) => $dropdown = $(dropdown) @@ -12,6 +14,67 @@ class @UsersSelect firstUser = $dropdown.data('first-user') selectedId = $dropdown.data('selected') defaultLabel = $dropdown.data('default-label') + issueURL = $dropdown.data('issueUpdate') + $selectbox = $dropdown.closest('.selectbox') + $block = $selectbox.closest('.block') + abilityName = $dropdown.data('ability-name') + $value = $block.find('.value') + $loading = $block.find('.block-loading').fadeOut() + + $block.on('click', '.js-assign-yourself', (e) => + e.preventDefault() + assignTo(@currentUser.id) + ) + + assignTo = (selected) -> + data = {} + data[abilityName] = {} + data[abilityName].assignee_id = selected + $loading + .fadeIn() + $.ajax( + type: 'PUT' + dataType: 'json' + url: issueURL + data: data + ).done (data) -> + $loading.fadeOut() + $selectbox.hide() + + if data.assignee + user = + name: data.assignee.name + username: data.assignee.username + avatar: data.assignee.avatar_url + else + user = + name: 'Unassigned' + username: '' + avatar: '' + + $value.html(noAssigneeTemplate(user)) + $value.find('a').attr('href') + + noAssigneeTemplate = _.template( + '<% if (username) { %> + <a class="author_link " href="/u/<%= username %>"> + <% if( avatar ) { %> + <img width="32" class="avatar avatar-inline s32" alt="" src="<%= avatar %>"> + <% } %> + <span class="author"><%= name %></span> + <span class="username"> + @<%= username %> + </span> + </a> + <% } else { %> + <span class="assign-yourself"> + No assignee - + <a href="#" class="js-assign-yourself"> + assign yourself + </a> + </span> + <% } %>' + ) $dropdown.glDropdown( data: (term, callback) => @@ -57,20 +120,36 @@ class @UsersSelect fields: ['name', 'username'] selectable: true fieldName: $dropdown.data('field-name') + toggleLabel: (selected) -> if selected && 'id' of selected selected.name else defaultLabel + + inputId: 'issue_assignee_id' + + hidden: (e) -> + $selectbox.hide() + $value.show() + clicked: -> page = $('body').data 'page' isIssueIndex = page is 'projects:issues:index' isMRIndex = page is page is 'projects:merge_requests:index' + if $dropdown.hasClass('js-filter-bulk-update') + return if $dropdown.hasClass('js-filter-submit') and (isIssueIndex or isMRIndex) Issues.filterResults $dropdown.closest('form') else if $dropdown.hasClass 'js-filter-submit' $dropdown.closest('form').submit() + else + selected = $dropdown + .closest('.selectbox') + .find("input[name='#{$dropdown.data('field-name')}']").val() + assignTo(selected) + renderRow: (user) -> username = if user.username then "@#{user.username}" else "" avatar = if user.avatar_url then user.avatar_url else false @@ -87,17 +166,25 @@ class @UsersSelect if avatar img = "<img src='#{avatar}' class='avatar avatar-inline' width='30' />" - "<li> - <a href='#' class='dropdown-menu-user-link #{selected}'> - #{img} - <strong class='dropdown-menu-user-full-name'> - #{user.name} - </strong> - <span class='dropdown-menu-user-username'> - #{username} - </span> - </a> - </li>" + # split into three parts so we can remove the username section if nessesary + listWithName = "<li> + <a href='#' class='dropdown-menu-user-link #{selected}'> + #{img} + <strong class='dropdown-menu-user-full-name'> + #{user.name} + </strong>" + + listWithUserName = "<span class='dropdown-menu-user-username'> + #{username} + </span>" + listClosingTags = "</a> + </li>" + + + if username is '' + listWithUserName = '' + + listWithName + listWithUserName + listClosingTags ) $('.ajax-users-select').each (i, select) => diff --git a/app/assets/stylesheets/application.scss b/app/assets/stylesheets/application.scss index e2d590f4df4..69b3b6586de 100644 --- a/app/assets/stylesheets/application.scss +++ b/app/assets/stylesheets/application.scss @@ -10,6 +10,7 @@ *= require dropzone/basic *= require cal-heatmap *= require cropper.css + *= require animate */ /* diff --git a/app/assets/stylesheets/framework/dropdowns.scss b/app/assets/stylesheets/framework/dropdowns.scss index 2d616fc660c..11e1e4275ac 100644 --- a/app/assets/stylesheets/framework/dropdowns.scss +++ b/app/assets/stylesheets/framework/dropdowns.scss @@ -117,7 +117,7 @@ padding-left: 10px; padding-right: 10px; color: $dropdown-link-color; - line-height: 34px; + line-height: 16px; text-overflow: ellipsis; border-radius: 2px; white-space: nowrap; @@ -167,13 +167,13 @@ } .dropdown-menu-user-link { - padding-top: 7px; + padding-top: 10px; padding-bottom: 7px; } .dropdown-menu-user-full-name { display: block; - font-weight: 600; + font-weight: 500; line-height: 16px; text-overflow: ellipsis; overflow: hidden; diff --git a/app/assets/stylesheets/framework/files.scss b/app/assets/stylesheets/framework/files.scss index ad0e88cda86..a26ace5cc19 100644 --- a/app/assets/stylesheets/framework/files.scss +++ b/app/assets/stylesheets/framework/files.scss @@ -3,12 +3,10 @@ * */ .file-holder { - border: none; border: 1px solid $border-color; &.readme-holder { - margin-top: 10px; - border-bottom: 0; + margin: $gl-padding-top 0; } table { diff --git a/app/assets/stylesheets/pages/commits.scss b/app/assets/stylesheets/pages/commits.scss index 5e91496679a..b6011fe7679 100644 --- a/app/assets/stylesheets/pages/commits.scss +++ b/app/assets/stylesheets/pages/commits.scss @@ -93,7 +93,6 @@ li.commit { .commit-row-info { color: $gl-gray; line-height: 24px; - font-size: 13px; a { color: $gl-gray; diff --git a/app/assets/stylesheets/pages/issuable.scss b/app/assets/stylesheets/pages/issuable.scss index 5300bb52a1b..88c1b614c74 100644 --- a/app/assets/stylesheets/pages/issuable.scss +++ b/app/assets/stylesheets/pages/issuable.scss @@ -30,6 +30,10 @@ } .issuable-sidebar { + a { + color: inherit; + } + .block { @include clearfix; padding: $gl-padding 0; @@ -89,7 +93,7 @@ } .cross-project-reference { - color: $gl-link-color; + color: inherit; span { white-space: nowrap; @@ -133,6 +137,12 @@ .value { line-height: 1; + + .assign-yourself { + margin-top: 10px; + font-weight: normal; + display: block; + } } .bold { @@ -252,6 +262,15 @@ text-decoration: none; } } + + .dropdown-menu-toggle { + width: 100%; + padding-top: 6px; + } + + .open .dropdown-menu { + width: 100%; + } } .btn-default.gutter-toggle { diff --git a/app/assets/stylesheets/pages/note_form.scss b/app/assets/stylesheets/pages/note_form.scss index daf2651425f..655f88b0c2c 100644 --- a/app/assets/stylesheets/pages/note_form.scss +++ b/app/assets/stylesheets/pages/note_form.scss @@ -71,8 +71,6 @@ } .note-form-actions { - background: #fff; - .note-form-option { margin-top: 8px; margin-left: 30px; diff --git a/app/assets/stylesheets/pages/projects.scss b/app/assets/stylesheets/pages/projects.scss index 71bde1174ee..ee7c6a3d55f 100644 --- a/app/assets/stylesheets/pages/projects.scss +++ b/app/assets/stylesheets/pages/projects.scss @@ -222,7 +222,7 @@ padding: 0; background: transparent; border: none; - line-height: 42px; + line-height: 36px; margin: 0; > li + li:before { diff --git a/app/controllers/projects/issues_controller.rb b/app/controllers/projects/issues_controller.rb index 877b39c9b1b..6d649e72f84 100644 --- a/app/controllers/projects/issues_controller.rb +++ b/app/controllers/projects/issues_controller.rb @@ -68,7 +68,13 @@ class Projects::IssuesController < Projects::ApplicationController @merge_requests = @issue.referenced_merge_requests(current_user) @related_branches = @issue.related_branches - @merge_requests.map(&:source_branch) - respond_with(@issue) + respond_to do |format| + format.html + format.json do + render json: @issue.to_json(include: [:milestone, :labels]) + end + end + end def create @@ -107,10 +113,7 @@ class Projects::IssuesController < Projects::ApplicationController end end format.json do - render json: { - saved: @issue.valid?, - assignee_avatar_url: @issue.assignee.try(:avatar_url) - } + render json: @issue.to_json(include: [:milestone, :labels, assignee: { methods: :avatar_url }]) end end end diff --git a/app/controllers/projects/merge_requests_controller.rb b/app/controllers/projects/merge_requests_controller.rb index b830d777752..6189de09f27 100644 --- a/app/controllers/projects/merge_requests_controller.rb +++ b/app/controllers/projects/merge_requests_controller.rb @@ -154,10 +154,7 @@ class Projects::MergeRequestsController < Projects::ApplicationController @merge_request.target_project, @merge_request]) end format.json do - render json: { - saved: @merge_request.valid?, - assignee_avatar_url: @merge_request.assignee.try(:avatar_url) - } + render json: @merge_request.to_json(include: [:milestone, :labels, :assignee]) end end else diff --git a/app/controllers/projects/milestones_controller.rb b/app/controllers/projects/milestones_controller.rb index b2e974eff17..5b0a63a933c 100644 --- a/app/controllers/projects/milestones_controller.rb +++ b/app/controllers/projects/milestones_controller.rb @@ -19,7 +19,6 @@ class Projects::MilestonesController < Projects::ApplicationController end @milestones = @milestones.includes(:project) - respond_to do |format| format.html do @milestones = @milestones.page(params[:page]) diff --git a/app/helpers/dropdowns_helper.rb b/app/helpers/dropdowns_helper.rb index 316a10b7da3..14697f774cc 100644 --- a/app/helpers/dropdowns_helper.rb +++ b/app/helpers/dropdowns_helper.rb @@ -60,7 +60,7 @@ module DropdownsHelper title_output << content_tag(:span, title) title_output << content_tag(:button, class: "dropdown-title-button dropdown-menu-close", aria: { label: "Close" }, type: "button") do - icon('times') + icon('times', class: 'dropdown-menu-close-icon') end title_output.html_safe diff --git a/app/helpers/issuables_helper.rb b/app/helpers/issuables_helper.rb index 81df2094392..62050691a39 100644 --- a/app/helpers/issuables_helper.rb +++ b/app/helpers/issuables_helper.rb @@ -16,6 +16,16 @@ module IssuablesHelper base_issuable_scope(issuable).where('iid > ?', issuable.iid).last end + def issuable_json_path(issuable) + project = issuable.project + + if issuable.kind_of?(MergeRequest) + namespace_project_merge_request_path(project.namespace, project, issuable.iid, :json) + else + namespace_project_issue_path(project.namespace, project, issuable.iid, :json) + end + end + def prev_issuable_for(issuable) base_issuable_scope(issuable).where('iid < ?', issuable.iid).first end diff --git a/app/views/projects/find_file/show.html.haml b/app/views/projects/find_file/show.html.haml index 905f6bbbd48..1fe1d98bf13 100644 --- a/app/views/projects/find_file/show.html.haml +++ b/app/views/projects/find_file/show.html.haml @@ -2,7 +2,7 @@ - header_title project_title(@project, "Files", project_files_path(@project)) .file-finder-holder.tree-holder.clearfix - .gray-content-block.top-block + .nav-block .tree-ref-holder = render 'shared/ref_switcher', destination: 'find_file', path: @path %ul.breadcrumb.repo-breadcrumb diff --git a/app/views/projects/merge_requests/_show.html.haml b/app/views/projects/merge_requests/_show.html.haml index ee5b9fd95a8..1dd8f721f7e 100644 --- a/app/views/projects/merge_requests/_show.html.haml +++ b/app/views/projects/merge_requests/_show.html.haml @@ -10,7 +10,7 @@ .merge-request{'data-url' => merge_request_path(@merge_request)} = render "projects/merge_requests/show/mr_title" - .merge-request-details.issuable-details + .merge-request-details.issuable-details{data: {id: @merge_request.project.id}} = render "projects/merge_requests/show/mr_box" .append-bottom-default.mr-source-target.prepend-top-default - if @merge_request.open? diff --git a/app/views/projects/tree/_tree_header.html.haml b/app/views/projects/tree/_tree_header.html.haml index ba69569b1e7..1c5f8b3928b 100644 --- a/app/views/projects/tree/_tree_header.html.haml +++ b/app/views/projects/tree/_tree_header.html.haml @@ -15,11 +15,11 @@ - if current_user %li - if !on_top_of_branch? - %span.btn.btn-sm.add-to-tree.disabled.has-tooltip{title: "You can only add files when you are on a branch", data: { container: 'body' }} + %span.btn.add-to-tree.disabled.has-tooltip{title: "You can only add files when you are on a branch", data: { container: 'body' }} = icon('plus') - else %span.dropdown - %a.dropdown-toggle.btn.btn-sm.add-to-tree{href: '#', "data-toggle" => "dropdown"} + %a.dropdown-toggle.btn.add-to-tree{href: '#', "data-toggle" => "dropdown"} = icon('plus') %ul.dropdown-menu - if can_edit_tree? diff --git a/app/views/shared/issuable/_filter.html.haml b/app/views/shared/issuable/_filter.html.haml index f91ff0e3694..c99da92be9f 100644 --- a/app/views/shared/issuable/_filter.html.haml +++ b/app/views/shared/issuable/_filter.html.haml @@ -9,13 +9,13 @@ .filter-item.inline - if params[:author_id] = hidden_field_tag(:author_id, params[:author_id]) - = dropdown_tag(user_dropdown_label(params[:author_id], "Author"), options: { toggle_class: "js-user-search js-filter-submit js-author-search", title: "Filter by author", filter: true, dropdown_class: "dropdown-menu-user dropdown-menu-selectable dropdown-menu-author", + = dropdown_tag(user_dropdown_label(params[:author_id], "Author"), options: { toggle_class: "js-user-search js-filter-submit js-author-search", title: "Filter by author", filter: true, dropdown_class: "dropdown-menu-user dropdown-menu-selectable dropdown-menu-author js-filter-submit", placeholder: "Search authors", data: { any_user: "Any Author", first_user: (current_user.username if current_user), current_user: true, project_id: (@project.id if @project), selected: params[:author_id], field_name: "author_id", default_label: "Author" } }) .filter-item.inline - if params[:assignee_id] = hidden_field_tag(:assignee_id, params[:assignee_id]) - = dropdown_tag(user_dropdown_label(params[:assignee_id], "Assignee"), options: { toggle_class: "js-user-search js-filter-submit js-assignee-search", title: "Filter by assignee", filter: true, dropdown_class: "dropdown-menu-user dropdown-menu-selectable dropdown-menu-assignee", + = dropdown_tag(user_dropdown_label(params[:assignee_id], "Assignee"), options: { toggle_class: "js-user-search js-filter-submit js-assignee-search", title: "Filter by assignee", filter: true, dropdown_class: "dropdown-menu-user dropdown-menu-selectable dropdown-menu-assignee js-filter-submit", placeholder: "Search assignee", data: { any_user: "Any Assignee", first_user: (current_user.username if current_user), null_user: true, current_user: true, project_id: (@project.id if @project), selected: params[:assignee_id], field_name: "assignee_id", default_label: "Assignee" } }) .filter-item.inline.milestone-filter @@ -23,7 +23,6 @@ .filter-item.inline.labels-filter = render "shared/issuable/label_dropdown" - .pull-right = render 'shared/sort_dropdown' @@ -38,11 +37,10 @@ %li %a{href: "#", data: {id: "close"}} Closed .filter-item.inline - = dropdown_tag("Assignee", options: { toggle_class: "js-user-search js-update-assignee", title: "Assign to", filter: true, dropdown_class: "dropdown-menu-user dropdown-menu-selectable", + = dropdown_tag("Assignee", options: { toggle_class: "js-user-search js-update-assignee js-filter-submit js-filter-bulk-update", title: "Assign to", filter: true, dropdown_class: "dropdown-menu-user dropdown-menu-selectable", placeholder: "Search authors", data: { first_user: (current_user.username if current_user), null_user: true, current_user: true, project_id: @project.id, field_name: "update[assignee_id]" } }) .filter-item.inline - = dropdown_tag("Milestone", options: { title: "Assign milestone", toggle_class: 'js-milestone-select', filter: true, dropdown_class: "dropdown-menu-selectable dropdown-menu-milestone", - placeholder: "Search milestones", data: { show_no: true, field_name: "update[milestone_id]", project_id: @project.id, milestones: namespace_project_milestones_path(@project.namespace, @project, :json), use_id: true } }) + = dropdown_tag("Milestone", options: { title: "Assign milestone", toggle_class: 'js-milestone-select js-extra-options js-filter-submit js-filter-bulk-update', filter: true, dropdown_class: "dropdown-menu-selectable dropdown-menu-milestone", placeholder: "Search milestones", data: { show_no: true, field_name: "update[milestone_id]", project_id: @project.id, milestones: namespace_project_milestones_path(@project.namespace, @project, :json), use_id: true } }) = hidden_field_tag 'update[issues_ids]', [] = hidden_field_tag :state_event, params[:state_event] .filter-item.inline diff --git a/app/views/shared/issuable/_milestone_dropdown.html.haml b/app/views/shared/issuable/_milestone_dropdown.html.haml index 0434506c8d7..1c79494f816 100644 --- a/app/views/shared/issuable/_milestone_dropdown.html.haml +++ b/app/views/shared/issuable/_milestone_dropdown.html.haml @@ -1,6 +1,6 @@ - if params[:milestone_title] = hidden_field_tag(:milestone_title, params[:milestone_title]) -= dropdown_tag(h(params[:milestone_title].presence || "Milestone"), options: { title: "Filter by milestone", toggle_class: 'js-milestone-select js-filter-submit', filter: true, dropdown_class: "dropdown-menu-selectable", += dropdown_tag(h(params[:milestone_title].presence || "Milestone"), options: { title: "Filter by milestone", toggle_class: 'js-milestone-select js-filter-submit js-extra-options', filter: true, dropdown_class: "dropdown-menu-selectable", placeholder: "Search milestones", footer_content: @project.present?, data: { show_no: true, show_any: true, field_name: "milestone_title", selected: params[:milestone_title], project_id: @project.try(:id), milestones: milestones_filter_dropdown_path, default_label: "Milestone" } }) do - if @project %ul.dropdown-footer-list diff --git a/app/views/shared/issuable/_sidebar.html.haml b/app/views/shared/issuable/_sidebar.html.haml index 0e20e86356d..451c64da2c4 100644 --- a/app/views/shared/issuable/_sidebar.html.haml +++ b/app/views/shared/issuable/_sidebar.html.haml @@ -28,6 +28,7 @@ = icon('user') .title.hide-collapsed Assignee + = icon('spinner spin', class: 'block-loading') - if can?(current_user, :"admin_#{issuable.to_ability_name}", @project) = link_to 'Edit', '#', class: 'edit-link pull-right' .value.bold.hide-collapsed @@ -39,10 +40,14 @@ %span.username = issuable.assignee.to_reference - else - .light None + %span.assign-yourself + No assignee - + %a.js-assign-yourself{ href: '#' } + assign yourself .selectbox.hide-collapsed - = users_select_tag("#{issuable.class.table_name.singularize}[assignee_id]", placeholder: 'Select assignee', class: 'custom-form-control js-select2 js-assignee', selected: issuable.assignee_id, project: @target_project, null_user: true, current_user: true, first_user: true) + = f.hidden_field 'assignee_id', value: issuable.assignee_id, id: 'issue_assignee_id' + = dropdown_tag('Select assignee', 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 if current_user), current_user: true, project_id: (@project.id if @project), field_name: "#{issuable.to_ability_name}[assignee_id]", issue_update: issuable_json_path(issuable), ability_name: issuable.to_ability_name, null_user: true } }) .block.milestone .sidebar-collapsed-icon @@ -54,6 +59,7 @@ No .title.hide-collapsed Milestone + = icon('spinner spin', class: 'block-loading') - if can?(current_user, :"admin_#{issuable.to_ability_name}", @project) = link_to 'Edit', '#', class: 'edit-link pull-right' .value.bold.hide-collapsed @@ -62,10 +68,10 @@ = issuable.milestone.title - else .light None + .selectbox.hide-collapsed - = f.select(:milestone_id, milestone_options(issuable), { include_blank: true }, { class: 'select2 select2-compact js-select2 js-milestone', data: { placeholder: 'Select milestone' }}) - = hidden_field_tag :issuable_context - = f.submit class: 'btn hide' + = 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: namespace_project_milestones_path(@project.namespace, @project, :json), ability_name: issuable.to_ability_name, issue_update: issuable_json_path(issuable), use_id: true }}) - if issuable.project.labels.any? .block.labels @@ -75,6 +81,7 @@ = issuable.labels.count .title.hide-collapsed Labels + = icon('spinner spin', class: 'block-loading') - if can?(current_user, :"admin_#{issuable.to_ability_name}", @project) = link_to 'Edit', '#', class: 'edit-link pull-right' .value.bold.issuable-show-labels.hide-collapsed{ class: ("has-labels" if issuable.labels.any?) } @@ -84,8 +91,31 @@ - else .light None .selectbox.hide-collapsed - = f.collection_select :label_ids, issuable.project.labels.all, :id, :name, - { selected: issuable.label_ids }, multiple: true, class: 'select2 js-select2', data: { placeholder: "Select labels" } + - issuable.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{type: "button", data: {toggle: "dropdown", field_name: "#{issuable.to_ability_name}[label_names][]", ability_name: issuable.to_ability_name, show_no: "true", show_any: "true", project_id: (@project.id if @project), issue_update: issuable_json_path(issuable), labels: (namespace_project_labels_path(@project.namespace, @project, :json) if @project)}} + %span.dropdown-toggle-text + Label + = icon('chevron-down') + .dropdown-menu.dropdown-select.dropdown-menu-paging.dropdown-menu-labels.dropdown-menu-selectable + .dropdown-page-one + = dropdown_title("Assign labels") + = dropdown_filter("Search labels") + = dropdown_content + - if @project + = dropdown_footer do + %ul.dropdown-footer-list + - if can? current_user, :admin_label, @project + %li + %a.dropdown-toggle-page{href: "#"} + Create new + %li + = link_to namespace_project_labels_path(@project.namespace, @project) do + - if can? current_user, :admin_label, @project + Manage labels + - else + View labels = render "shared/issuable/participants", participants: issuable.participants(current_user) - if current_user @@ -116,5 +146,7 @@ = clipboard_button(clipboard_text: project_ref) :javascript - new Subscription('.subscription'); - new IssuableContext(); + new MilestoneSelect('{"namespace":"#{@project.namespace.path}","path":"#{@project.path}"}'); + new LabelsSelect(); + new IssuableContext('#{current_user.to_json(only: [:username, :id, :name])}'); + new Subscription('.subscription') |