diff options
Diffstat (limited to 'sonar-server/src/main/coffee/quality-gate')
18 files changed, 887 insertions, 0 deletions
diff --git a/sonar-server/src/main/coffee/quality-gate/app.coffee b/sonar-server/src/main/coffee/quality-gate/app.coffee new file mode 100644 index 00000000000..db18db98711 --- /dev/null +++ b/sonar-server/src/main/coffee/quality-gate/app.coffee @@ -0,0 +1,142 @@ +requirejs.config + baseUrl: "#{baseUrl}/js" + + paths: + 'jquery': 'third-party/jquery' + 'backbone': 'third-party/backbone' + 'backbone.marionette': 'third-party/backbone.marionette' + 'handlebars': 'third-party/handlebars' + 'moment': 'third-party/moment' + 'select-list': 'common/select-list' + + shim: + 'backbone.marionette': + deps: ['backbone'] + exports: 'Marionette' + 'backbone': + exports: 'Backbone' + 'handlebars': + exports: 'Handlebars' + 'moment': + exports: 'moment' + 'select-list': + exports: 'SelectList' + + +requirejs [ + 'backbone', 'backbone.marionette', 'handlebars', + 'quality-gate/collections/quality-gates', + 'quality-gate/views/quality-gate-sidebar-list-view', + 'quality-gate/views/quality-gate-actions-view', + 'quality-gate/views/quality-gate-edit-view', + 'quality-gate/router', + 'quality-gate/layout', + 'common/handlebars-extensions' +], ( + Backbone, Marionette, Handlebars, + QualityGates, + QualityGateSidebarListItemView, + QualityGateActionsView, + QualityGateEditView, + QualityGateRouter, + QualityGateLayout +) -> + + # Create a generic error handler for ajax requests + jQuery.ajaxSetup + error: (jqXHR) -> + text = jqXHR.responseText + errorBox = jQuery('.modal-error') + if jqXHR.responseJSON?.errors? + text = _.pluck(jqXHR.responseJSON.errors, 'msg').join '. ' + if errorBox.length > 0 + errorBox.show().text text + else + alert text + + + # Add html class to mark the page as navigator page + jQuery('html').addClass('navigator-page quality-gates-page'); + + + # Create a Quality Gate Application + App = new Marionette.Application + + + App.qualityGates = new QualityGates + + + App.openFirstQualityGate = -> + if @qualityGates.length > 0 + @router.navigate "show/#{@qualityGates.models[0].get('id')}", trigger: true + else + App.layout.detailsRegion.reset() + + + App.deleteQualityGate = (id) -> + App.qualityGates.remove id + App.openFirstQualityGate() + + + App.unsetDefaults = (id) -> + App.qualityGates.each (gate) -> + gate.set('default', false) unless gate.id == id + + + # Construct layout + App.addInitializer -> + @layout = new QualityGateLayout app: @ + jQuery('body').append @layout.render().el + + + # Construct actions bar + App.addInitializer -> + @codingRulesHeaderView = new QualityGateActionsView + app: @ + @layout.actionsRegion.show @codingRulesHeaderView + + + # Construct sidebar + App.addInitializer -> + @qualityGateSidebarListView = new QualityGateSidebarListItemView + collection: @qualityGates + app: @ + @layout.resultsRegion.show @qualityGateSidebarListView + + + # Construct edit view + App.addInitializer -> + @qualityGateEditView = new QualityGateEditView app: @ + @qualityGateEditView.render() + + + # Start router + App.addInitializer -> + @router = new QualityGateRouter app: @ + Backbone.history.start() + + + # Open first quality gate when come to the page + App.addInitializer -> + initial = Backbone.history.fragment == '' + App.openFirstQualityGate() if initial + + + # Call app, Load metrics and the list of quality gates before start the application + appXHR = jQuery.ajax + url: "#{baseUrl}/api/qualitygates/app" + .done (r) => + App.canEdit = r.edit + App.periods = r.periods + App.metrics = r.metrics + window.messages = r.messages + + qualityGatesXHR = App.qualityGates.fetch() + + jQuery.when(qualityGatesXHR, appXHR) + .done -> + # Remove the initial spinner + jQuery('#quality-gate-page-loader').remove() + + # Start the application + App.start() diff --git a/sonar-server/src/main/coffee/quality-gate/collections/conditions.coffee b/sonar-server/src/main/coffee/quality-gate/collections/conditions.coffee new file mode 100644 index 00000000000..8899fc14405 --- /dev/null +++ b/sonar-server/src/main/coffee/quality-gate/collections/conditions.coffee @@ -0,0 +1,11 @@ +define [ + 'backbone', + 'quality-gate/models/condition' +], ( + Backbone, + Condition +) -> + + class Conditions extends Backbone.Collection + model: Condition + comparator: 'metric' diff --git a/sonar-server/src/main/coffee/quality-gate/collections/quality-gates.coffee b/sonar-server/src/main/coffee/quality-gate/collections/quality-gates.coffee new file mode 100644 index 00000000000..1d1c8542481 --- /dev/null +++ b/sonar-server/src/main/coffee/quality-gate/collections/quality-gates.coffee @@ -0,0 +1,30 @@ +define [ + 'backbone', + 'quality-gate/models/quality-gate' +], ( + Backbone, + QualityGate +) -> + + class QualityGates extends Backbone.Collection + model: QualityGate + + + url: -> + "#{baseUrl}/api/qualitygates/list" + + + # { + # "qualitygates": [ + # { "id": 42, "name": "QG 1" }, + # { "id": 43, "name": "QG 2" }, + # { "id": 44, "name": "QG 3" } + # ], + # "default": 42 + # } + parse: (r) -> + r.qualitygates.map (gate) -> + _.extend gate, default: gate.id == r.default + + + comparator: (item) -> item.get('name').toLowerCase() diff --git a/sonar-server/src/main/coffee/quality-gate/layout.coffee b/sonar-server/src/main/coffee/quality-gate/layout.coffee new file mode 100644 index 00000000000..ff7f6f54bbd --- /dev/null +++ b/sonar-server/src/main/coffee/quality-gate/layout.coffee @@ -0,0 +1,35 @@ +define [ + 'backbone.marionette', + 'templates/quality-gates' +], ( + Marionette, + Templates +) -> + + class AppLayout extends Marionette.Layout + className: 'navigator quality-gates-navigator' + template: Templates['quality-gates-layout'] + + + regions: + headerRegion: '.navigator-header' + actionsRegion: '.navigator-actions' + resultsRegion: '.navigator-results' + detailsRegion: '.navigator-details' + + + initialize: (options) -> + @listenTo options.app.qualityGates, 'all', @updateLayout + + + updateLayout: -> + empty = @options.app.qualityGates.length == 0 + @$(@headerRegion.el).toggle !empty + @$(@detailsRegion.el).toggle !empty + + + onRender: -> + @updateLayout() + + # Adjust details region height + @$(@detailsRegion.el).css 'bottom', jQuery('#footer').outerHeight() diff --git a/sonar-server/src/main/coffee/quality-gate/models/condition.coffee b/sonar-server/src/main/coffee/quality-gate/models/condition.coffee new file mode 100644 index 00000000000..1fe2c634a41 --- /dev/null +++ b/sonar-server/src/main/coffee/quality-gate/models/condition.coffee @@ -0,0 +1,42 @@ +define [ + 'backbone' +], ( + Backbone +) -> + + class Condition extends Backbone.Model + + url: -> + "#{baseUrl}/api/qualitygates/create_condition" + + + save: -> + method = unless @isNew() then 'update' else 'create' + data = + metric: @get('metric').key + op: @get('op') + warning: @get('warning') + error: @get('error') + + unless @get('period') == '0' + data.period = @get('period') + + unless @isNew() + data.id = @id + else + data.gateId = @get('gateId') + + jQuery.ajax({ + url: "#{baseUrl}/api/qualitygates/#{method}_condition" + type: 'POST' + data: data + }).done (r) => + @set 'id', r.id + + + delete: -> + jQuery.ajax + url: "#{baseUrl}/api/qualitygates/delete_condition" + type: 'POST' + data: id: @id + diff --git a/sonar-server/src/main/coffee/quality-gate/models/quality-gate.coffee b/sonar-server/src/main/coffee/quality-gate/models/quality-gate.coffee new file mode 100644 index 00000000000..5acffc066e7 --- /dev/null +++ b/sonar-server/src/main/coffee/quality-gate/models/quality-gate.coffee @@ -0,0 +1,10 @@ +define [ + 'backbone' +], ( + Backbone +) -> + + class QualityGate extends Backbone.Model + + url: -> + "#{baseUrl}/api/qualitygates/show?id=#{@get('id')}" diff --git a/sonar-server/src/main/coffee/quality-gate/router.coffee b/sonar-server/src/main/coffee/quality-gate/router.coffee new file mode 100644 index 00000000000..f21161a97bf --- /dev/null +++ b/sonar-server/src/main/coffee/quality-gate/router.coffee @@ -0,0 +1,42 @@ +define [ + 'backbone', + 'quality-gate/models/quality-gate', + 'quality-gate/views/quality-gate-detail-view', + 'quality-gate/views/quality-gate-detail-header-view', +], ( + Backbone, + QualityGate, + QualityGateDetailView, + QualityGateDetailHeaderView +) -> + + class QualityGateRouter extends Backbone.Router + + routes: + 'show/:id': 'show' + + + initialize: (options) -> + @app = options.app + + + show: (id) -> + qualityGate = @app.qualityGates.get id + if qualityGate + @app.qualityGateSidebarListView.highlight id + + qualityGateDetailHeaderView = new QualityGateDetailHeaderView + app: @app + model: qualityGate + @app.layout.headerRegion.show qualityGateDetailHeaderView + + qualityGateDetailView = new QualityGateDetailView + app: @app + model: qualityGate + @app.layout.detailsRegion.show qualityGateDetailView + qualityGateDetailView.$el.hide() + + qualityGateDetailHeaderView.showSpinner() + qualityGate.fetch().done -> + qualityGateDetailView.$el.show() + qualityGateDetailHeaderView.hideSpinner() diff --git a/sonar-server/src/main/coffee/quality-gate/views/quality-gate-actions-view.coffee b/sonar-server/src/main/coffee/quality-gate/views/quality-gate-actions-view.coffee new file mode 100644 index 00000000000..6025a3c5b3c --- /dev/null +++ b/sonar-server/src/main/coffee/quality-gate/views/quality-gate-actions-view.coffee @@ -0,0 +1,27 @@ +define [ + 'backbone.marionette', + 'templates/quality-gates' + 'quality-gate/models/quality-gate' +], ( + Marionette, + Templates + QualityGate +) -> + + class QualityGateActionsView extends Marionette.ItemView + template: Templates['quality-gate-actions'] + + + events: + 'click #quality-gate-add': 'add' + + + add: -> + qualityGate = new QualityGate() + @options.app.qualityGateEditView.method = 'create' + @options.app.qualityGateEditView.model = qualityGate + @options.app.qualityGateEditView.show() + + + serializeData: -> + _.extend super, canEdit: @options.app.canEdit diff --git a/sonar-server/src/main/coffee/quality-gate/views/quality-gate-detail-condition-view.coffee b/sonar-server/src/main/coffee/quality-gate/views/quality-gate-detail-condition-view.coffee new file mode 100644 index 00000000000..e928b169b25 --- /dev/null +++ b/sonar-server/src/main/coffee/quality-gate/views/quality-gate-detail-condition-view.coffee @@ -0,0 +1,123 @@ +define [ + 'backbone.marionette', + 'templates/quality-gates' +], ( + Marionette, + Templates +) -> + + class QualityGateDetailConditionView extends Marionette.ItemView + tagName: 'tr' + template: Templates['quality-gate-detail-condition'] + spinner: '<i class="spinner"></i>' + + + modelEvents: + 'change:id': 'render' + + + ui: + periodSelect: '[name=period]' + operatorSelect: '[name=operator]' + warningInput: '[name=warning]' + errorInput: '[name=error]' + actionsBox: '.quality-gate-condition-actions' + updateButton: '.update-condition' + + + events: + 'click @ui.updateButton': 'saveCondition' + 'click .delete-condition': 'deleteCondition' + 'click .add-condition': 'saveCondition' + 'click .cancel-add-condition': 'cancelAddCondition' + 'keyup :input': 'enableUpdate' + 'change :input': 'enableUpdate' + + + initialize: -> + @populateMetric() + + + populateMetric: -> + metricKey = @model.get('metric') + metric = _.findWhere @options.app.metrics, key: metricKey + if metric? + switch metric.type + when 'WORK_DUR' then metric.placeholder = '1d 7h 59min' + when 'RATING' then metric.placeholder = 'A' + @model.set { metric: metric }, { silent: true } + @model.set { isDiffMetric: metric.key.indexOf('new_') == 0 }, { silent: true } + + + onRender: -> + @ui.periodSelect.val @model.get('period') || '0' + @ui.operatorSelect.val @model.get('op') + @ui.warningInput.val @model.get('warning') + @ui.errorInput.val @model.get('error') + + @ui.periodSelect.select2 + allowClear: false + minimumResultsForSearch: 999 + width: '200px' + + @ui.operatorSelect.select2 + allowClear: false + minimumResultsForSearch: 999 + width: '150px' + + @ui.periodSelect.select2('open') if @model.isNew() + + + showSpinner: -> + jQuery(@spinner).prependTo @ui.actionsBox + @ui.actionsBox.find(':not(.spinner)').hide() + + + hideSpinner: -> + @ui.actionsBox.find('.spinner').remove() + @ui.actionsBox.find(':not(.spinner)').show() + + + saveCondition: -> + @showSpinner() + @model.set + period: @ui.periodSelect.val() + op: @ui.operatorSelect.val() + warning: @ui.warningInput.val() + error: @ui.errorInput.val() + @model.save() + .always => + @ui.updateButton.prop 'disabled', true + @hideSpinner() + .done => + @options.collectionView.updateConditions() + + + deleteCondition: -> + if confirm t('quality_gates.delete_condition.confirm.message') + @showSpinner() + @model.delete().done => + @options.collectionView.collection.remove @model + @options.collectionView.updateConditions() + @close() + + + cancelAddCondition: -> + @close() + + + enableUpdate: -> + @ui.updateButton.prop 'disabled', false + + + serializeData: -> + period = _.findWhere(@options.app.periods, key: this.model.get('period')) + data = _.extend super, + canEdit: @options.app.canEdit + periods: @options.app.periods + periodText: period?.text + unless @options.app.canEdit + _.extend data, + warning: jQuery('<input>').data('type', @model.get('metric').type).val(@model.get('warning')).originalVal() + error: jQuery('<input>').data('type', @model.get('metric').type).val(@model.get('error')).originalVal() + data diff --git a/sonar-server/src/main/coffee/quality-gate/views/quality-gate-detail-conditions-empty-view.coffee b/sonar-server/src/main/coffee/quality-gate/views/quality-gate-detail-conditions-empty-view.coffee new file mode 100644 index 00000000000..9c0362f4e6a --- /dev/null +++ b/sonar-server/src/main/coffee/quality-gate/views/quality-gate-detail-conditions-empty-view.coffee @@ -0,0 +1,11 @@ +define [ + 'backbone.marionette', + 'templates/quality-gates' +], ( + Marionette, + Templates +) -> + + class QualityGateDetailConditionsView extends Marionette.ItemView + tagName: 'tr' + template: Templates['quality-gate-detail-conditions-empty'] diff --git a/sonar-server/src/main/coffee/quality-gate/views/quality-gate-detail-conditions-view.coffee b/sonar-server/src/main/coffee/quality-gate/views/quality-gate-detail-conditions-view.coffee new file mode 100644 index 00000000000..7b76a55ae61 --- /dev/null +++ b/sonar-server/src/main/coffee/quality-gate/views/quality-gate-detail-conditions-view.coffee @@ -0,0 +1,86 @@ +define [ + 'backbone.marionette', + 'templates/quality-gates' + 'quality-gate/models/condition', + 'quality-gate/views/quality-gate-detail-condition-view', + 'quality-gate/views/quality-gate-detail-conditions-empty-view' +], ( + Marionette, + Templates + Condition, + QualityGateDetailConditionView, + QualityGateDetailConditionsEmptyView, +) -> + + class QualityGateDetailConditionsView extends Marionette.CompositeView + template: Templates['quality-gate-detail-conditions'] + itemView: QualityGateDetailConditionView + emptyView: QualityGateDetailConditionsEmptyView + itemViewContainer: '.quality-gate-conditions tbody' + + + ui: + metricSelect: '#quality-gate-new-condition-metric' + introductionShowMore: '.quality-gate-introduction-show-more' + introductionMore: '.quality-gate-introduction-more' + + + events: + 'click @ui.introductionShowMore': 'showMoreIntroduction' + 'change @ui.metricSelect': 'addCondition' + + + itemViewOptions: -> + app: @options.app + collectionView: @ + + + appendHtml: (compositeView, itemView) -> + if (compositeView.isBuffering) + compositeView.elBuffer.appendChild itemView.el + compositeView._bufferedChildren.push itemView + else + container = @getItemViewContainer compositeView + container.prepend itemView.el + + + onRender: -> + @ui.introductionMore.hide() + @ui.metricSelect.select2 + allowClear: false, + width: '250px', + placeholder: t('alerts.select_metric') + + + groupedMetrics: -> + metrics = @options.app.metrics + metrics = _.groupBy metrics, 'domain' + metrics = _.map metrics, (metrics, domain) -> + domain: domain, metrics: _.sortBy metrics, 'short_name' + _.sortBy metrics, 'domain' + + + serializeData: -> + _.extend super, + canEdit: @options.app.canEdit + metricGroups: @groupedMetrics() + + + showMoreIntroduction: -> + @ui.introductionShowMore.hide() + @ui.introductionMore.show() + + + addCondition: -> + metric = @ui.metricSelect.val() + @ui.metricSelect.select2('val', '') + condition = new Condition + metric: metric + gateId: @options.gateId + @collection.unshift condition + + + updateConditions: -> + conditions = @collection.map (item) -> _.extend item.toJSON(), + metric: item.get('metric').key + @options.qualityGate.set { conditions: conditions }, { silent: true } diff --git a/sonar-server/src/main/coffee/quality-gate/views/quality-gate-detail-header-view.coffee b/sonar-server/src/main/coffee/quality-gate/views/quality-gate-detail-header-view.coffee new file mode 100644 index 00000000000..acf593f149f --- /dev/null +++ b/sonar-server/src/main/coffee/quality-gate/views/quality-gate-detail-header-view.coffee @@ -0,0 +1,90 @@ +define [ + 'backbone.marionette', + 'templates/quality-gates', + 'quality-gate/models/quality-gate' +], ( + Marionette, + Templates + QualityGate +) -> + + class QualityGateDetailHeaderView extends Marionette.ItemView + template: Templates['quality-gate-detail-header'] + spinner: '<i class="spinner"></i>' + + + modelEvents: + 'change': 'render' + + + events: + 'click #quality-gate-rename': 'renameQualityGate' + 'click #quality-gate-copy': 'copyQualityGate' + 'click #quality-gate-delete': 'deleteQualityGate' + 'click #quality-gate-set-as-default': 'setAsDefault' + 'click #quality-gate-unset-as-default': 'unsetAsDefault' + + + renameQualityGate: -> + @options.app.qualityGateEditView.method = 'rename' + @options.app.qualityGateEditView.model = @model + @options.app.qualityGateEditView.show() + + + copyQualityGate: -> + copiedModel = new QualityGate @model.toJSON() + copiedModel.set 'default', false + @options.app.qualityGateEditView.method = 'copy' + @options.app.qualityGateEditView.model = copiedModel + @options.app.qualityGateEditView.show() + + + deleteQualityGate: -> + message = if @model.get 'default' then 'quality_gates.delete.confirm.default' else 'quality_gates.delete.confirm.message' + if confirm t(message).replace('{0}', @model.get 'name') + @showSpinner() + jQuery.ajax + type: 'POST' + url: "#{baseUrl}/api/qualitygates/destroy" + data: id: @model.id + .always => + @hideSpinner() + .done => + @options.app.deleteQualityGate @model.id + + + changeDefault: (set) -> + @showSpinner() + data = if set then { id: @model.id } else {} + method = if set then 'set_as_default' else 'unset_default' + jQuery.ajax + type: 'POST' + url: "#{baseUrl}/api/qualitygates/#{method}" + data: data + .always => + @hideSpinner() + .done => + @options.app.unsetDefaults @model.id + @model.set 'default', !@model.get('default') + + + setAsDefault: -> + @changeDefault true + + + unsetAsDefault: -> + @changeDefault false + + + showSpinner: -> + @$el.hide() + jQuery(@spinner).insertBefore @$el + + + hideSpinner: -> + @$el.prev().remove() + @$el.show() + + + serializeData: -> + _.extend super, canEdit: @options.app.canEdit diff --git a/sonar-server/src/main/coffee/quality-gate/views/quality-gate-detail-projects-view.coffee b/sonar-server/src/main/coffee/quality-gate/views/quality-gate-detail-projects-view.coffee new file mode 100644 index 00000000000..afa85c6bad7 --- /dev/null +++ b/sonar-server/src/main/coffee/quality-gate/views/quality-gate-detail-projects-view.coffee @@ -0,0 +1,38 @@ +define [ + 'backbone.marionette', + 'templates/quality-gates' + 'select-list' +], ( + Marionette, + Templates +) -> + + class QualityGateDetailProjectsView extends Marionette.ItemView + template: Templates['quality-gate-detail-projects'] + + + onRender: -> + unless @model.get('default') + new SelectList + el: @$('#select-list-projects') + width: '100%' + readOnly: !@options.app.canEdit + format: (item) -> item.name + searchUrl: "#{baseUrl}/api/qualitygates/search?gateId=#{@options.gateId}" + selectUrl: "#{baseUrl}/api/qualitygates/select" + deselectUrl: "#{baseUrl}/api/qualitygates/deselect" + extra: + gateId: @options.gateId + selectParameter: 'projectId' + selectParameterValue: 'id' + labels: + selected: t('quality_gates.projects.with') + deselected: t('quality_gates.projects.without') + all: t('quality_gates.projects.all') + noResults: t('quality_gates.projects.noResults') + tooltips: + select: t('quality_gates.projects.select_hint') + deselect: t('quality_gates.projects.deselect_hint') + + serializeData: -> + _.extend super, canEdit: @options.app.canEdit diff --git a/sonar-server/src/main/coffee/quality-gate/views/quality-gate-detail-view.coffee b/sonar-server/src/main/coffee/quality-gate/views/quality-gate-detail-view.coffee new file mode 100644 index 00000000000..ff2ca9c70ea --- /dev/null +++ b/sonar-server/src/main/coffee/quality-gate/views/quality-gate-detail-view.coffee @@ -0,0 +1,50 @@ +define [ + 'backbone.marionette', + 'templates/quality-gates', + 'quality-gate/collections/conditions', + 'quality-gate/views/quality-gate-detail-header-view', + 'quality-gate/views/quality-gate-detail-conditions-view', + 'quality-gate/views/quality-gate-detail-projects-view' +], ( + Marionette, + Templates, + Conditions, + QualityGateDetailHeaderView, + QualityGateDetailConditionsView, + QualityGateDetailProjectsView +) -> + + class QualityGateDetailView extends Marionette.Layout + template: Templates['quality-gate-detail'] + + + regions: + conditionsRegion: '#quality-gate-conditions' + projectsRegion: '#quality-gate-projects' + + + modelEvents: + 'change': 'render' + + + onRender: -> + @showConditions() + @showProjects() + + + showConditions: -> + conditions = new Conditions @model.get('conditions') + view = new QualityGateDetailConditionsView + app: @options.app + collection: conditions + gateId: @model.id + qualityGate: @model + @conditionsRegion.show view + + + showProjects: -> + view = new QualityGateDetailProjectsView + app: @options.app + model: @model + gateId: @model.id + @projectsRegion.show view diff --git a/sonar-server/src/main/coffee/quality-gate/views/quality-gate-edit-view.coffee b/sonar-server/src/main/coffee/quality-gate/views/quality-gate-edit-view.coffee new file mode 100644 index 00000000000..47e0bbd9101 --- /dev/null +++ b/sonar-server/src/main/coffee/quality-gate/views/quality-gate-edit-view.coffee @@ -0,0 +1,86 @@ +define [ + 'backbone.marionette', + 'templates/quality-gates' +], ( + Marionette, + Templates +) -> + + class QualityGateEditView extends Marionette.ItemView + className: 'modal' + template: Templates['quality-gate-edit'] + + + ui: + nameInput: '#quality-gate-edit-name' + + + events: + 'submit form': 'onSubmit' + 'click #quality-gate-cancel-create': 'hide' + + + onRender: -> + @$el.dialog + dialogClass: 'no-close', + width: '600px', + draggable: false, + autoOpen: false, + modal: true, + minHeight: 50, + resizable: false, + title: null + + + show: -> + @render() + @$el.dialog 'open' + @ui.nameInput.focus() + + + hide: -> + @$el.dialog 'close' + + + saveRequest: (data) -> + jQuery.ajax + type: 'POST' + url: "#{baseUrl}/api/qualitygates/#{@method}" + data: data + .done => @hide() + + + onSubmit: (e) -> + e.preventDefault() + switch @method + when 'create' then @createQualityGate() + when 'copy' then @copyQualityGate() + when 'rename' then @saveQualityGate() + else + + + createQualityGate: -> + data = name: @ui.nameInput.val() + @saveRequest(data).done (r) => + @model.set id: r.id, name: r.name + @options.app.qualityGates.add @model + @options.app.router.navigate "show/#{r.id}", trigger: true + + + saveQualityGate: -> + data = id: @model.id, name: @ui.nameInput.val() + @saveRequest(data).done (r) => + @model.set name: r.name + + + copyQualityGate: -> + data = id: @model.id, name: @ui.nameInput.val() + @saveRequest(data).done (r) => + @model.set id: r.id, name: r.name + @options.app.qualityGates.add @model + @options.app.router.navigate "show/#{r.id}", trigger: true + + + serializeData: -> + if @model + _.extend @model.toJSON(), method: @method diff --git a/sonar-server/src/main/coffee/quality-gate/views/quality-gate-sidebar-list-empty-view.coffee b/sonar-server/src/main/coffee/quality-gate/views/quality-gate-sidebar-list-empty-view.coffee new file mode 100644 index 00000000000..c828e4b72f2 --- /dev/null +++ b/sonar-server/src/main/coffee/quality-gate/views/quality-gate-sidebar-list-empty-view.coffee @@ -0,0 +1,12 @@ +define [ + 'backbone.marionette', + 'templates/quality-gates' +], ( + Marionette, + Templates +) -> + + class QualityGateSidebarListEmptyView extends Marionette.ItemView + tagName: 'li' + className: 'empty' + template: Templates['quality-gate-sidebar-list-empty'] diff --git a/sonar-server/src/main/coffee/quality-gate/views/quality-gate-sidebar-list-item-view.coffee b/sonar-server/src/main/coffee/quality-gate/views/quality-gate-sidebar-list-item-view.coffee new file mode 100644 index 00000000000..70cd6d15366 --- /dev/null +++ b/sonar-server/src/main/coffee/quality-gate/views/quality-gate-sidebar-list-item-view.coffee @@ -0,0 +1,27 @@ +define [ + 'backbone.marionette', + 'templates/quality-gates' +], ( + Marionette, + Templates +) -> + + class QualityGateSidebarListItemView extends Marionette.ItemView + tagName: 'li' + template: Templates['quality-gate-sidebar-list-item'] + + + modelEvents: + 'change': 'render' + + + events: + 'click': 'showQualityGate' + + + onRender: -> + @$el.toggleClass 'active', @options.highlighted + + + showQualityGate: -> + @options.app.router.navigate "show/#{@model.id}", trigger: true diff --git a/sonar-server/src/main/coffee/quality-gate/views/quality-gate-sidebar-list-view.coffee b/sonar-server/src/main/coffee/quality-gate/views/quality-gate-sidebar-list-view.coffee new file mode 100644 index 00000000000..43f92f74d27 --- /dev/null +++ b/sonar-server/src/main/coffee/quality-gate/views/quality-gate-sidebar-list-view.coffee @@ -0,0 +1,25 @@ +define [ + 'backbone.marionette', + 'quality-gate/views/quality-gate-sidebar-list-item-view', + 'quality-gate/views/quality-gate-sidebar-list-empty-view' +], ( + Marionette, + QualityGateSidebarListItemView, + QualityGateSidebarListEmptyView, +) -> + + class QualityGateSidebarListView extends Marionette.CollectionView + tagName: 'ol' + className: 'navigator-results-list' + itemView: QualityGateSidebarListItemView + emptyView: QualityGateSidebarListEmptyView + + + itemViewOptions: (model) -> + app: @options.app + highlighted: model.get('id') == +@highlighted + + + highlight: (id) -> + @highlighted = id + @render() |