aboutsummaryrefslogtreecommitdiffstats
path: root/server/sonar-web/src/main/js/apps/metrics
diff options
context:
space:
mode:
authorStas Vilchik <vilchiks@gmail.com>2015-06-08 12:20:03 +0200
committerStas Vilchik <vilchiks@gmail.com>2015-06-08 17:00:05 +0200
commit42c8a155eccbabdab673d8ae496ed3c615d34147 (patch)
tree437928ed4c175fa8bb73071e27c25b8f97cb060c /server/sonar-web/src/main/js/apps/metrics
parent4a2247c24efee48de53ca07302b6810ab7205621 (diff)
downloadsonarqube-42c8a155eccbabdab673d8ae496ed3c615d34147.tar.gz
sonarqube-42c8a155eccbabdab673d8ae496ed3c615d34147.zip
SONAR-6624 refactor custom metrics page
Diffstat (limited to 'server/sonar-web/src/main/js/apps/metrics')
-rw-r--r--server/sonar-web/src/main/js/apps/metrics/app.js61
-rw-r--r--server/sonar-web/src/main/js/apps/metrics/create-view.js33
-rw-r--r--server/sonar-web/src/main/js/apps/metrics/delete-view.js32
-rw-r--r--server/sonar-web/src/main/js/apps/metrics/form-view.js57
-rw-r--r--server/sonar-web/src/main/js/apps/metrics/header-view.js27
-rw-r--r--server/sonar-web/src/main/js/apps/metrics/layout.js15
-rw-r--r--server/sonar-web/src/main/js/apps/metrics/list-footer-view.js34
-rw-r--r--server/sonar-web/src/main/js/apps/metrics/list-item-view.js50
-rw-r--r--server/sonar-web/src/main/js/apps/metrics/list-view.js18
-rw-r--r--server/sonar-web/src/main/js/apps/metrics/metric.js37
-rw-r--r--server/sonar-web/src/main/js/apps/metrics/metrics.js41
-rw-r--r--server/sonar-web/src/main/js/apps/metrics/templates/metrics-delete.hbs13
-rw-r--r--server/sonar-web/src/main/js/apps/metrics/templates/metrics-form.hbs36
-rw-r--r--server/sonar-web/src/main/js/apps/metrics/templates/metrics-header.hbs10
-rw-r--r--server/sonar-web/src/main/js/apps/metrics/templates/metrics-layout.hbs5
-rw-r--r--server/sonar-web/src/main/js/apps/metrics/templates/metrics-list-footer.hbs6
-rw-r--r--server/sonar-web/src/main/js/apps/metrics/templates/metrics-list-item.hbs23
-rw-r--r--server/sonar-web/src/main/js/apps/metrics/update-view.js32
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);
+ });
+ }
+ });
+
+});