diff options
author | Stas Vilchik <vilchiks@gmail.com> | 2015-06-08 12:20:03 +0200 |
---|---|---|
committer | Stas Vilchik <vilchiks@gmail.com> | 2015-06-08 17:00:05 +0200 |
commit | 42c8a155eccbabdab673d8ae496ed3c615d34147 (patch) | |
tree | 437928ed4c175fa8bb73071e27c25b8f97cb060c /server/sonar-web/src/main/js/apps/metrics | |
parent | 4a2247c24efee48de53ca07302b6810ab7205621 (diff) | |
download | sonarqube-42c8a155eccbabdab673d8ae496ed3c615d34147.tar.gz sonarqube-42c8a155eccbabdab673d8ae496ed3c615d34147.zip |
SONAR-6624 refactor custom metrics page
Diffstat (limited to 'server/sonar-web/src/main/js/apps/metrics')
18 files changed, 530 insertions, 0 deletions
diff --git a/server/sonar-web/src/main/js/apps/metrics/app.js b/server/sonar-web/src/main/js/apps/metrics/app.js new file mode 100644 index 00000000000..4792cd7464e --- /dev/null +++ b/server/sonar-web/src/main/js/apps/metrics/app.js @@ -0,0 +1,61 @@ +define([ + './layout', + './metrics', + './header-view', + './list-view', + './list-footer-view' +], function (Layout, Metrics, HeaderView, ListView, ListFooterView) { + + var $ = jQuery, + App = new Marionette.Application(), + init = function (options) { + // Layout + this.layout = new Layout({ el: options.el }); + this.layout.render(); + + // Collection + this.metrics = new Metrics(); + + // Header View + this.headerView = new HeaderView({ + collection: this.metrics, + domains: this.domains, + types: this.types + }); + this.layout.headerRegion.show(this.headerView); + + // List View + this.listView = new ListView({ + collection: this.metrics, + domains: this.domains, + types: this.types + }); + this.layout.listRegion.show(this.listView); + + // List Footer View + this.listFooterView = new ListFooterView({ collection: this.metrics }); + this.layout.listFooterRegion.show(this.listFooterView); + + // Go! + this.metrics.fetch(); + }, + requestDomains = function () { + return $.get(baseUrl + '/api/metrics/domains').done(function (r) { + App.domains = r.domains; + }); + }, + requestTypes = function () { + return $.get(baseUrl + '/api/metrics/types').done(function (r) { + App.types = r.types; + }); + }; + + App.on('start', function (options) { + $.when(window.requestMessages(), requestDomains(), requestTypes()).done(function () { + init.call(App, options); + }); + }); + + return App; + +}); diff --git a/server/sonar-web/src/main/js/apps/metrics/create-view.js b/server/sonar-web/src/main/js/apps/metrics/create-view.js new file mode 100644 index 00000000000..0db9fa70f11 --- /dev/null +++ b/server/sonar-web/src/main/js/apps/metrics/create-view.js @@ -0,0 +1,33 @@ +define([ + './metric', + './form-view' +], function (Metric, FormView) { + + return FormView.extend({ + + sendRequest: function () { + var that = this, + metric = new Metric({ + key: this.$('#create-metric-key').val(), + name: this.$('#create-metric-name').val(), + description: this.$('#create-metric-description').val(), + domain: this.$('#create-metric-domain').val(), + type: this.$('#create-metric-type').val() + }); + this.disableForm(); + return metric.save(null, { + statusCode: { + // do not show global error + 400: null + } + }).done(function () { + that.collection.refresh(); + that.close(); + }).fail(function (jqXHR) { + that.enableForm(); + that.showErrors([{ msg: jqXHR.responseJSON.err_msg }]); + }); + } + }); + +}); diff --git a/server/sonar-web/src/main/js/apps/metrics/delete-view.js b/server/sonar-web/src/main/js/apps/metrics/delete-view.js new file mode 100644 index 00000000000..0acc3c055a9 --- /dev/null +++ b/server/sonar-web/src/main/js/apps/metrics/delete-view.js @@ -0,0 +1,32 @@ +define([ + 'components/common/modal-form', + './templates' +], function (ModalForm) { + + return ModalForm.extend({ + template: Templates['metrics-delete'], + + onFormSubmit: function (e) { + this._super(e); + this.sendRequest(); + }, + + sendRequest: function () { + var that = this, + collection = this.model.collection; + return this.model.destroy({ + wait: true, + statusCode: { + // do not show global error + 400: null + } + }).done(function () { + collection.refresh(); + that.close(); + }).fail(function (jqXHR) { + that.showErrors(jqXHR.responseJSON.errors, jqXHR.responseJSON.warnings); + }); + } + }); + +}); diff --git a/server/sonar-web/src/main/js/apps/metrics/form-view.js b/server/sonar-web/src/main/js/apps/metrics/form-view.js new file mode 100644 index 00000000000..77b5f2a54a5 --- /dev/null +++ b/server/sonar-web/src/main/js/apps/metrics/form-view.js @@ -0,0 +1,57 @@ +define([ + 'components/common/modal-form', + './templates' +], function (ModalForm) { + + var $ = jQuery; + + return ModalForm.extend({ + template: Templates['metrics-form'], + + onRender: function () { + var that = this; + this._super(); + this.$('[data-toggle="tooltip"]').tooltip({ container: 'body', placement: 'bottom' }); + this.$('#create-metric-domain').select2({ + width: '250px', + createSearchChoice: function (term) { + return { id: term, text: '+' + term }; + }, + createSearchChoicePosition: 'top', + initSelection: function (element, callback) { + var value = $(element).val(); + callback({ id: value, text: value }); + }, + query: function (options) { + var items = that.options.domains.filter(function (d) { + return d.toLowerCase().indexOf(options.term.toLowerCase()) !== -1; + }), + results = items.map(function (item) { + return { id: item, text: item }; + }); + options.callback({ results: results, more: false }); + } + }).select2('val', this.model && this.model.get('domain')); + this.$('#create-metric-type').select2({ width: '250px' }); + }, + + onClose: function () { + this._super(); + this.$('[data-toggle="tooltip"]').tooltip('destroy'); + }, + + onFormSubmit: function (e) { + this._super(e); + this.sendRequest(); + }, + + serializeData: function () { + return _.extend(this._super(), { + domains: this.options.domains, + types: this.options.types + }); + } + + }); + +}); diff --git a/server/sonar-web/src/main/js/apps/metrics/header-view.js b/server/sonar-web/src/main/js/apps/metrics/header-view.js new file mode 100644 index 00000000000..aed1d449218 --- /dev/null +++ b/server/sonar-web/src/main/js/apps/metrics/header-view.js @@ -0,0 +1,27 @@ +define([ + './create-view', + './templates' +], function (CreateView) { + + return Marionette.ItemView.extend({ + template: Templates['metrics-header'], + + events: { + 'click #metrics-create': 'onCreateClick' + }, + + onCreateClick: function (e) { + e.preventDefault(); + this.createMetric(); + }, + + createMetric: function () { + new CreateView({ + collection: this.collection, + domains: this.options.domains, + types: this.options.types + }).render(); + } + }); + +}); diff --git a/server/sonar-web/src/main/js/apps/metrics/layout.js b/server/sonar-web/src/main/js/apps/metrics/layout.js new file mode 100644 index 00000000000..812212a42fa --- /dev/null +++ b/server/sonar-web/src/main/js/apps/metrics/layout.js @@ -0,0 +1,15 @@ +define([ + './templates' +], function () { + + return Marionette.Layout.extend({ + template: Templates['metrics-layout'], + + regions: { + headerRegion: '#metrics-header', + listRegion: '#metrics-list', + listFooterRegion: '#metrics-list-footer' + } + }); + +}); diff --git a/server/sonar-web/src/main/js/apps/metrics/list-footer-view.js b/server/sonar-web/src/main/js/apps/metrics/list-footer-view.js new file mode 100644 index 00000000000..932dfd6d35f --- /dev/null +++ b/server/sonar-web/src/main/js/apps/metrics/list-footer-view.js @@ -0,0 +1,34 @@ +define([ + './templates' +], function () { + + return Marionette.ItemView.extend({ + template: Templates['metrics-list-footer'], + + collectionEvents: { + 'all': 'render' + }, + + events: { + 'click #metrics-fetch-more': 'onMoreClick' + }, + + onMoreClick: function (e) { + e.preventDefault(); + this.fetchMore(); + }, + + fetchMore: function () { + this.collection.fetchMore(); + }, + + serializeData: function () { + return _.extend(this._super(), { + total: this.collection.total, + count: this.collection.length, + more: this.collection.hasMore() + }); + } + }); + +}); diff --git a/server/sonar-web/src/main/js/apps/metrics/list-item-view.js b/server/sonar-web/src/main/js/apps/metrics/list-item-view.js new file mode 100644 index 00000000000..224ab4c700e --- /dev/null +++ b/server/sonar-web/src/main/js/apps/metrics/list-item-view.js @@ -0,0 +1,50 @@ +define([ + './update-view', + './delete-view', + './templates' +], function (UpdateView, DeleteView) { + + return Marionette.ItemView.extend({ + tagName: 'li', + className: 'panel panel-vertical', + template: Templates['metrics-list-item'], + + events: { + 'click .js-metric-update': 'onUpdateClick', + 'click .js-metric-delete': 'onDeleteClick' + }, + + onRender: function () { + this.$el.attr('data-id', this.model.id); + this.$('[data-toggle="tooltip"]').tooltip({ container: 'body', placement: 'bottom' }); + }, + + onClose: function () { + this.$('[data-toggle="tooltip"]').tooltip('destroy'); + }, + + onUpdateClick: function (e) { + e.preventDefault(); + this.updateMetric(); + }, + + onDeleteClick: function (e) { + e.preventDefault(); + this.deleteMetric(); + }, + + updateMetric: function () { + new UpdateView({ + model: this.model, + collection: this.model.collection, + types: this.options.types, + domains: this.options.domains + }).render(); + }, + + deleteMetric: function () { + new DeleteView({ model: this.model }).render(); + } + }); + +}); diff --git a/server/sonar-web/src/main/js/apps/metrics/list-view.js b/server/sonar-web/src/main/js/apps/metrics/list-view.js new file mode 100644 index 00000000000..27060bbe7d4 --- /dev/null +++ b/server/sonar-web/src/main/js/apps/metrics/list-view.js @@ -0,0 +1,18 @@ +define([ + './list-item-view', + './templates' +], function (ListItemView) { + + return Marionette.CollectionView.extend({ + tagName: 'ul', + itemView: ListItemView, + + itemViewOptions: function () { + return { + types: this.options.types, + domains: this.options.domains + }; + } + }); + +}); diff --git a/server/sonar-web/src/main/js/apps/metrics/metric.js b/server/sonar-web/src/main/js/apps/metrics/metric.js new file mode 100644 index 00000000000..cb160c882d0 --- /dev/null +++ b/server/sonar-web/src/main/js/apps/metrics/metric.js @@ -0,0 +1,37 @@ +define(function () { + + return Backbone.Model.extend({ + idAttribute: 'id', + + urlRoot: function () { + return baseUrl + '/api/metrics'; + }, + + sync: function (method, model, options) { + var opts = options || {}; + if (method === 'create') { + _.defaults(opts, { + url: this.urlRoot() + '/create', + type: 'POST', + data: _.pick(model.toJSON(), 'key', 'name', 'description', 'domain', 'type') + }); + } + if (method === 'update') { + _.defaults(opts, { + url: this.urlRoot() + '/update', + type: 'POST', + data: _.pick(model.toJSON(), 'id', 'key', 'name', 'description', 'domain', 'type') + }); + } + if (method === 'delete') { + _.defaults(opts, { + url: this.urlRoot() + '/delete', + type: 'POST', + data: { ids: this.id } + }); + } + return Backbone.ajax(opts); + } + }); + +}); diff --git a/server/sonar-web/src/main/js/apps/metrics/metrics.js b/server/sonar-web/src/main/js/apps/metrics/metrics.js new file mode 100644 index 00000000000..393ebe3c2b1 --- /dev/null +++ b/server/sonar-web/src/main/js/apps/metrics/metrics.js @@ -0,0 +1,41 @@ +define([ + './metric' +], function (Metric) { + + return Backbone.Collection.extend({ + model: Metric, + + url: function () { + return baseUrl + '/api/metrics/search'; + }, + + parse: function (r) { + this.total = r.total; + this.p = r.p; + this.ps = r.ps; + return r.metrics; + }, + + fetch: function (options) { + var opts = _.defaults(options || {}, { data: {} }); + this.q = opts.data.q; + opts.data.isCustom = true; + return this._super(opts); + }, + + fetchMore: function () { + var p = this.p + 1; + return this.fetch({ add: true, remove: false, data: { p: p, ps: this.ps, q: this.q } }); + }, + + refresh: function () { + return this.fetch({ reset: true, data: { q: this.q } }); + }, + + hasMore: function () { + return this.total > this.p * this.ps; + } + + }); + +}); diff --git a/server/sonar-web/src/main/js/apps/metrics/templates/metrics-delete.hbs b/server/sonar-web/src/main/js/apps/metrics/templates/metrics-delete.hbs new file mode 100644 index 00000000000..73cddffcbf0 --- /dev/null +++ b/server/sonar-web/src/main/js/apps/metrics/templates/metrics-delete.hbs @@ -0,0 +1,13 @@ +<form id="delete-metric-form"> + <div class="modal-head"> + <h2>Delete Metric</h2> + </div> + <div class="modal-body"> + <div class="js-modal-messages"></div> + Are you sure you want to delete metric "{{name}}"? + </div> + <div class="modal-foot"> + <button id="delete-metric-submit" class="button-red">Delete</button> + <a href="#" class="js-modal-close" id="delete-metric-cancel">Cancel</a> + </div> +</form> diff --git a/server/sonar-web/src/main/js/apps/metrics/templates/metrics-form.hbs b/server/sonar-web/src/main/js/apps/metrics/templates/metrics-form.hbs new file mode 100644 index 00000000000..43c533f8416 --- /dev/null +++ b/server/sonar-web/src/main/js/apps/metrics/templates/metrics-form.hbs @@ -0,0 +1,36 @@ +<form id="create-metric-form" autocomplete="off"> + <div class="modal-head"> + <h2>{{#if id}}Update{{else}}Create{{/if}} Metric</h2> + </div> + <div class="modal-body"> + <div class="js-modal-messages"></div> + <div class="modal-field"> + <label for="create-metric-key">Key<em class="mandatory">*</em></label> + <input id="create-metric-key" name="key" type="text" maxlength="200" required value="{{key}}"> + </div> + <div class="modal-field"> + <label for="create-metric-name">Name<em class="mandatory">*</em></label> + <input id="create-metric-name" name="name" type="text" maxlength="200" required value="{{name}}"> + </div> + <div class="modal-field"> + <label for="create-metric-description">Description</label> + <textarea id="create-metric-description" name="description">{{description}}</textarea> + </div> + <div class="modal-field"> + <label for="create-metric-domain">Domain</label> + <input id="create-metric-domain" name="domain" type="text" maxlength="200" value="{{domain}}"> + </div> + <div class="modal-field"> + <label for="create-metric-type">Type<em class="mandatory">*</em></label> + <select id="create-metric-type" name="type"> + {{#each types}} + <option value="{{key}}" {{#eq key ../type.key}}selected{{/eq}}>{{name}}</option> + {{/each}} + </select> + </div> + </div> + <div class="modal-foot"> + <button id="create-metric-submit">{{#if id}}Update{{else}}Create{{/if}}</button> + <a href="#" class="js-modal-close" id="create-metric-cancel">Cancel</a> + </div> +</form> diff --git a/server/sonar-web/src/main/js/apps/metrics/templates/metrics-header.hbs b/server/sonar-web/src/main/js/apps/metrics/templates/metrics-header.hbs new file mode 100644 index 00000000000..05ef5f04fad --- /dev/null +++ b/server/sonar-web/src/main/js/apps/metrics/templates/metrics-header.hbs @@ -0,0 +1,10 @@ +<header class="page-header"> + <h1 class="page-title">Custom Metrics</h1> + <div class="page-actions"> + <div class="button-group"> + <button id="metrics-create">Create Metric</button> + </div> + </div> + <p class="page-description">These metrics are available for all projects. Manual measures can be set at project level + via the configuration interface.</p> +</header> diff --git a/server/sonar-web/src/main/js/apps/metrics/templates/metrics-layout.hbs b/server/sonar-web/src/main/js/apps/metrics/templates/metrics-layout.hbs new file mode 100644 index 00000000000..e1f1fd2b20f --- /dev/null +++ b/server/sonar-web/src/main/js/apps/metrics/templates/metrics-layout.hbs @@ -0,0 +1,5 @@ +<div class="page"> + <div id="metrics-header"></div> + <div id="metrics-list"></div> + <div id="metrics-list-footer"></div> +</div> diff --git a/server/sonar-web/src/main/js/apps/metrics/templates/metrics-list-footer.hbs b/server/sonar-web/src/main/js/apps/metrics/templates/metrics-list-footer.hbs new file mode 100644 index 00000000000..c389b85f818 --- /dev/null +++ b/server/sonar-web/src/main/js/apps/metrics/templates/metrics-list-footer.hbs @@ -0,0 +1,6 @@ +<footer class="spacer-top note text-center"> + {{count}}/{{total}} shown + {{#if more}} + <a id="metrics-fetch-more" class="spacer-left" href="#">show more</a> + {{/if}} +</footer> diff --git a/server/sonar-web/src/main/js/apps/metrics/templates/metrics-list-item.hbs b/server/sonar-web/src/main/js/apps/metrics/templates/metrics-list-item.hbs new file mode 100644 index 00000000000..d4a59f17d7b --- /dev/null +++ b/server/sonar-web/src/main/js/apps/metrics/templates/metrics-list-item.hbs @@ -0,0 +1,23 @@ +<div class="pull-right big-spacer-left nowrap"> + <a class="js-metric-update icon-edit" title="Update" data-toggle="tooltip" href="#"></a> + <a class="js-metric-delete icon-delete" title="Delete" data-toggle="tooltip" href="#"></a> +</div> + +<div class="display-inline-block text-top width-30"> + <div> + <strong class="js-metric-name">{{name}}</strong> + <span class="js-metric-key note little-spacer-left">{{key}}</span> + </div> +</div> + +<div class="display-inline-block text-top width-20"> + <span class="js-metric-domain">{{domain}}</span> +</div> + +<div class="display-inline-block text-top width-20"> + <span class="js-metric-type">{{type.name}}</span> +</div> + +<div class="display-inline-block text-top width-20"> + <span class="js-metric-description">{{description}}</span> +</div> diff --git a/server/sonar-web/src/main/js/apps/metrics/update-view.js b/server/sonar-web/src/main/js/apps/metrics/update-view.js new file mode 100644 index 00000000000..c4edec81a8d --- /dev/null +++ b/server/sonar-web/src/main/js/apps/metrics/update-view.js @@ -0,0 +1,32 @@ +define([ + './form-view' +], function (FormView) { + + return FormView.extend({ + + sendRequest: function () { + var that = this; + this.model.set({ + key: this.$('#create-metric-key').val(), + name: this.$('#create-metric-name').val(), + description: this.$('#create-metric-description').val(), + domain: this.$('#create-metric-domain').val(), + type: this.$('#create-metric-type').val() + }); + this.disableForm(); + return this.model.save(null, { + statusCode: { + // do not show global error + 400: null + } + }).done(function () { + that.collection.refresh(); + that.close(); + }).fail(function (jqXHR) { + that.enableForm(); + that.showErrors(jqXHR.responseJSON.errors, jqXHR.responseJSON.warnings); + }); + } + }); + +}); |