aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--it/it-tests/src/test/resources/qualityGate/QualityGateUiTest/should_display_quality_gates_page.html6
-rw-r--r--server/sonar-web/package.json1
-rw-r--r--server/sonar-web/src/main/js/api/quality-gates.js87
-rw-r--r--server/sonar-web/src/main/js/apps/quality-gates/app.js95
-rw-r--r--server/sonar-web/src/main/js/apps/quality-gates/components/AddConditionForm.js67
-rw-r--r--server/sonar-web/src/main/js/apps/quality-gates/components/Condition.js253
-rw-r--r--server/sonar-web/src/main/js/apps/quality-gates/components/Conditions.js104
-rw-r--r--server/sonar-web/src/main/js/apps/quality-gates/components/ConditionsAlert.js73
-rw-r--r--server/sonar-web/src/main/js/apps/quality-gates/components/Details.js138
-rw-r--r--server/sonar-web/src/main/js/apps/quality-gates/components/DetailsContent.js62
-rw-r--r--server/sonar-web/src/main/js/apps/quality-gates/components/DetailsHeader.js77
-rw-r--r--server/sonar-web/src/main/js/apps/quality-gates/components/Intro.js (renamed from server/sonar-web/src/main/js/apps/quality-gates/router.js)32
-rw-r--r--server/sonar-web/src/main/js/apps/quality-gates/components/List.js53
-rw-r--r--server/sonar-web/src/main/js/apps/quality-gates/components/ListHeader.js (renamed from server/sonar-web/src/main/js/apps/quality-gates/gate-view.js)44
-rw-r--r--server/sonar-web/src/main/js/apps/quality-gates/components/Projects.js (renamed from server/sonar-web/src/main/js/apps/quality-gates/gates.js)56
-rw-r--r--server/sonar-web/src/main/js/apps/quality-gates/components/QualityGatesApp.js81
-rw-r--r--server/sonar-web/src/main/js/apps/quality-gates/condition.js66
-rw-r--r--server/sonar-web/src/main/js/apps/quality-gates/conditions.js27
-rw-r--r--server/sonar-web/src/main/js/apps/quality-gates/containers/DetailsContainer.js56
-rw-r--r--server/sonar-web/src/main/js/apps/quality-gates/containers/QualityGatesAppContainer.js (renamed from server/sonar-web/src/main/js/apps/quality-gates/gates-view.js)37
-rw-r--r--server/sonar-web/src/main/js/apps/quality-gates/controller.js83
-rw-r--r--server/sonar-web/src/main/js/apps/quality-gates/copy-view.js41
-rw-r--r--server/sonar-web/src/main/js/apps/quality-gates/details-view.js72
-rw-r--r--server/sonar-web/src/main/js/apps/quality-gates/form-view.js66
-rw-r--r--server/sonar-web/src/main/js/apps/quality-gates/gate-condition-view.js118
-rw-r--r--server/sonar-web/src/main/js/apps/quality-gates/gate-conditions-empty-view.js35
-rw-r--r--server/sonar-web/src/main/js/apps/quality-gates/gate-conditions-view.js93
-rw-r--r--server/sonar-web/src/main/js/apps/quality-gates/gate-projects-view.js66
-rw-r--r--server/sonar-web/src/main/js/apps/quality-gates/gate.js73
-rw-r--r--server/sonar-web/src/main/js/apps/quality-gates/header-view.js71
-rw-r--r--server/sonar-web/src/main/js/apps/quality-gates/intro-view.js27
-rw-r--r--server/sonar-web/src/main/js/apps/quality-gates/layout.js46
-rw-r--r--server/sonar-web/src/main/js/apps/quality-gates/store/actions.js108
-rw-r--r--server/sonar-web/src/main/js/apps/quality-gates/store/reducers.js93
-rw-r--r--server/sonar-web/src/main/js/apps/quality-gates/store/utils.js51
-rw-r--r--server/sonar-web/src/main/js/apps/quality-gates/templates/_quality-gate-intro.hbs7
-rw-r--r--server/sonar-web/src/main/js/apps/quality-gates/templates/quality-gate-actions.hbs8
-rw-r--r--server/sonar-web/src/main/js/apps/quality-gates/templates/quality-gate-detail-condition.hbs69
-rw-r--r--server/sonar-web/src/main/js/apps/quality-gates/templates/quality-gate-detail-conditions-empty.hbs3
-rw-r--r--server/sonar-web/src/main/js/apps/quality-gates/templates/quality-gate-detail-conditions.hbs41
-rw-r--r--server/sonar-web/src/main/js/apps/quality-gates/templates/quality-gate-detail-header.hbs14
-rw-r--r--server/sonar-web/src/main/js/apps/quality-gates/templates/quality-gate-detail.hbs2
-rw-r--r--server/sonar-web/src/main/js/apps/quality-gates/templates/quality-gates-gate.hbs13
-rw-r--r--server/sonar-web/src/main/js/apps/quality-gates/templates/quality-gates-gates.hbs1
-rw-r--r--server/sonar-web/src/main/js/apps/quality-gates/templates/quality-gates-intro.hbs4
-rw-r--r--server/sonar-web/src/main/js/apps/quality-gates/templates/quality-gates-layout.hbs11
-rw-r--r--server/sonar-web/src/main/js/apps/quality-gates/views/copy-view.js (renamed from server/sonar-web/src/main/js/apps/quality-gates/gate-conditions-delete-view.js)32
-rw-r--r--server/sonar-web/src/main/js/apps/quality-gates/views/create-view.js (renamed from server/sonar-web/src/main/js/apps/quality-gates/create-view.js)36
-rw-r--r--server/sonar-web/src/main/js/apps/quality-gates/views/delete-view.js (renamed from server/sonar-web/src/main/js/apps/quality-gates/actions-view.js)31
-rw-r--r--server/sonar-web/src/main/js/apps/quality-gates/views/gate-conditions-delete-view.js (renamed from server/sonar-web/src/main/js/apps/quality-gates/delete-view.js)28
-rw-r--r--server/sonar-web/src/main/js/apps/quality-gates/views/gate-projects-view.js67
-rw-r--r--server/sonar-web/src/main/js/apps/quality-gates/views/rename-view.js (renamed from server/sonar-web/src/main/js/apps/quality-gates/rename-view.js)36
-rw-r--r--server/sonar-web/src/main/js/apps/quality-profiles/templates/quality-profiles-intro.hbs8
-rw-r--r--server/sonar-web/src/main/js/components/navigator/filters/ajax-select-filters.js12
-rw-r--r--server/sonar-web/src/main/js/components/navigator/filters/base-filters.js16
-rw-r--r--server/sonar-web/src/main/js/components/navigator/filters/choice-filters.js8
-rw-r--r--server/sonar-web/src/main/js/components/navigator/filters/favorite-filters.js2
-rw-r--r--server/sonar-web/src/main/js/components/navigator/filters/metric-filters.js2
-rw-r--r--server/sonar-web/src/main/js/components/navigator/filters/more-criteria-filters.js2
-rw-r--r--server/sonar-web/src/main/js/components/navigator/filters/range-filters.js10
-rw-r--r--server/sonar-web/src/main/js/components/navigator/filters/string-filters.js4
-rw-r--r--server/sonar-web/src/main/js/components/store/configureStore.js12
-rw-r--r--server/sonar-web/src/main/less/components/react-select.less4
-rw-r--r--server/sonar-web/src/main/less/components/search-navigator.less6
-rw-r--r--server/sonar-web/src/main/less/pages/quality-gates.less3
-rw-r--r--sonar-core/src/main/resources/org/sonar/l10n/core.properties11
66 files changed, 1643 insertions, 1318 deletions
diff --git a/it/it-tests/src/test/resources/qualityGate/QualityGateUiTest/should_display_quality_gates_page.html b/it/it-tests/src/test/resources/qualityGate/QualityGateUiTest/should_display_quality_gates_page.html
index a702ec9e45f..22e16831674 100644
--- a/it/it-tests/src/test/resources/qualityGate/QualityGateUiTest/should_display_quality_gates_page.html
+++ b/it/it-tests/src/test/resources/qualityGate/QualityGateUiTest/should_display_quality_gates_page.html
@@ -40,17 +40,17 @@
</tr>
<tr>
<td>waitForElementPresent</td>
- <td>css=.js-list a</td>
+ <td>css=.quality-gates-results a</td>
<td></td>
</tr>
<tr>
<td>click</td>
- <td>css=.js-list a</td>
+ <td>css=.quality-gates-results a</td>
<td></td>
</tr>
<tr>
<td>waitForElementPresent</td>
- <td>css=.js-conditions</td>
+ <td>id=quality-gate-conditions</td>
<td></td>
</tr>
</tbody>
diff --git a/server/sonar-web/package.json b/server/sonar-web/package.json
index 662070ef3f0..b68a902c342 100644
--- a/server/sonar-web/package.json
+++ b/server/sonar-web/package.json
@@ -57,6 +57,7 @@
"react-dom": "0.14.2",
"react-redux": "4.0.1",
"react-router": "2.0.0",
+ "react-router-redux": "^4.0.0",
"react-select": "1.0.0-beta6",
"redux": "3.0.5",
"redux-logger": "2.2.1",
diff --git a/server/sonar-web/src/main/js/api/quality-gates.js b/server/sonar-web/src/main/js/api/quality-gates.js
new file mode 100644
index 00000000000..a32b0295d1d
--- /dev/null
+++ b/server/sonar-web/src/main/js/api/quality-gates.js
@@ -0,0 +1,87 @@
+/*
+ * SonarQube
+ * Copyright (C) 2009-2016 SonarSource SA
+ * mailto:contact AT sonarsource DOT com
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+import { getJSON, post, postJSON } from '../helpers/request';
+
+export function fetchQualityGatesAppDetails () {
+ const url = '/api/qualitygates/app';
+
+ return getJSON(url);
+}
+
+export function fetchQualityGates () {
+ const url = '/api/qualitygates/list';
+
+ return getJSON(url).then(r => r.qualitygates.map(qualityGate => {
+ return {
+ ...qualityGate,
+ isDefault: qualityGate.id === r.default
+ };
+ }));
+}
+
+export function fetchQualityGate (id) {
+ const url = '/api/qualitygates/show';
+ return getJSON(url, { id });
+}
+
+export function createQualityGate (name) {
+ const url = '/api/qualitygates/create';
+ return postJSON(url, { name });
+}
+
+export function deleteQualityGate (id) {
+ const url = '/api/qualitygates/destroy';
+ return post(url, { id });
+}
+
+export function renameQualityGate (id, name) {
+ const url = '/api/qualitygates/rename';
+ return post(url, { id, name });
+}
+
+export function copyQualityGate (id, name) {
+ const url = '/api/qualitygates/copy';
+ return postJSON(url, { id, name });
+}
+
+export function setQualityGateAsDefault (id) {
+ const url = '/api/qualitygates/set_as_default';
+ return post(url, { id });
+}
+
+export function unsetQualityGateAsDefault (id) {
+ const url = '/api/qualitygates/unset_default';
+ return post(url, { id });
+}
+
+export function createCondition (gateId, condition) {
+ const url = '/api/qualitygates/create_condition';
+ return postJSON(url, { ...condition, gateId });
+}
+
+export function updateCondition (condition) {
+ const url = '/api/qualitygates/update_condition';
+ return postJSON(url, { ...condition });
+}
+
+export function deleteCondition (id) {
+ const url = '/api/qualitygates/delete_condition';
+ return post(url, { id });
+}
diff --git a/server/sonar-web/src/main/js/apps/quality-gates/app.js b/server/sonar-web/src/main/js/apps/quality-gates/app.js
index 4cae39632a8..43834a5ceea 100644
--- a/server/sonar-web/src/main/js/apps/quality-gates/app.js
+++ b/server/sonar-web/src/main/js/apps/quality-gates/app.js
@@ -17,66 +17,45 @@
* along with this program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
-import $ from 'jquery';
-import Backbone from 'backbone';
-import Marionette from 'backbone.marionette';
-import Gates from './gates';
-import GatesView from './gates-view';
-import ActionsView from './actions-view';
-import Router from './router';
-import Layout from './layout';
-import Controller from './controller';
-
-const App = new Marionette.Application();
-
-const init = function () {
- const options = window.sonarqube;
- // Layout
- this.layout = new Layout({ el: options.el });
- this.layout.render();
- $('#footer').addClass('search-navigator-footer');
-
- // Gates List
- this.gates = new Gates();
-
- // Controller
- this.controller = new Controller({ app: this });
-
- // Header
- this.actionsView = new ActionsView({
- canEdit: this.canEdit,
- collection: this.gates
- });
- this.layout.actionsRegion.show(this.actionsView);
-
- // List
- this.gatesView = new GatesView({
- canEdit: this.canEdit,
- collection: this.gates
+import React from 'react';
+import { render } from 'react-dom';
+import { Router, Route, IndexRoute, Redirect, useRouterHistory } from 'react-router';
+import { createHistory } from 'history';
+import { combineReducers } from 'redux';
+import { Provider } from 'react-redux';
+import { syncHistoryWithStore, routerReducer, routerMiddleware } from 'react-router-redux';
+
+import QualityGatesAppContainer from './containers/QualityGatesAppContainer';
+import Intro from './components/Intro';
+import DetailsContainer from './containers/DetailsContainer';
+import rootReducer from './store/reducers';
+import configureStore from '../../components/store/configureStore';
+
+window.sonarqube.appStarted.then(options => {
+ const el = document.querySelector(options.el);
+
+ const history = useRouterHistory(createHistory)({
+ basename: '/quality_gates'
});
- this.layout.resultsRegion.show(this.gatesView);
- // Router
- this.router = new Router({ app: this });
- Backbone.history.start({
- pushState: true,
- root: options.urlRoot
+ const finalReducer = combineReducers({
+ rootReducer,
+ routing: routerReducer
});
-};
-
-const appXHR = $.get('/api/qualitygates/app')
- .done(function (r) {
- App.canEdit = r.edit;
- App.periods = r.periods;
- App.metrics = r.metrics;
- });
-App.on('start', function (options) {
- appXHR.done(function () {
- init.call(App, options);
- });
+ const store = configureStore(finalReducer, [routerMiddleware(history)]);
+
+ const finalHistory = syncHistoryWithStore(history, store);
+
+ render((
+ <Provider store={store}>
+ <Router history={finalHistory}>
+ <Route path="/" component={QualityGatesAppContainer}>
+ <IndexRoute component={Intro}/>
+ <Route path="show/:id" component={DetailsContainer}/>
+ <Redirect from="/index" to="/"/>
+ </Route>
+ </Router>
+ </Provider>
+ ), el);
});
-
-window.sonarqube.appStarted.then(options => App.start(options));
-
-
diff --git a/server/sonar-web/src/main/js/apps/quality-gates/components/AddConditionForm.js b/server/sonar-web/src/main/js/apps/quality-gates/components/AddConditionForm.js
new file mode 100644
index 00000000000..6cd0229391e
--- /dev/null
+++ b/server/sonar-web/src/main/js/apps/quality-gates/components/AddConditionForm.js
@@ -0,0 +1,67 @@
+/*
+ * SonarQube
+ * Copyright (C) 2009-2016 SonarSource SA
+ * mailto:contact AT sonarsource DOT com
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+import _ from 'underscore';
+import React from 'react';
+import Select from 'react-select';
+
+import { translate } from '../../../helpers/l10n';
+
+export default function AddConditionForm ({ metrics, onSelect }) {
+ function handleChange (option) {
+ const metric = option.value;
+ // e.target.value = '';
+ onSelect(metric);
+ }
+
+ const metricsToDisplay = metrics.filter(metric => !metric.hidden);
+ const sortedMetrics = _.sortBy(metricsToDisplay, 'domain');
+ const options = sortedMetrics.map(metric => {
+ return {
+ value: metric.key,
+ label: metric.name,
+ domain: metric.domain
+ };
+ });
+
+ // use "disabled" property to emulate optgroups
+ const optionsWithDomains = [];
+ options.forEach((option, index, options) => {
+ const previous = index > 0 ? options[index - 1] : null;
+ if (!previous || previous.domain !== option.domain) {
+ optionsWithDomains.push({
+ value: option.domain,
+ label: option.domain,
+ disabled: true
+ });
+ }
+ optionsWithDomains.push(option);
+ });
+
+ return (
+ <div className="big-spacer-top panel bg-muted">
+ <Select
+ id="quality-gate-new-condition-metric"
+ className="text-middle input-large"
+ options={optionsWithDomains}
+ placeholder={translate('quality_gates.add_condition')}
+ onChange={handleChange}/>
+ </div>
+ );
+}
diff --git a/server/sonar-web/src/main/js/apps/quality-gates/components/Condition.js b/server/sonar-web/src/main/js/apps/quality-gates/components/Condition.js
new file mode 100644
index 00000000000..550b8905499
--- /dev/null
+++ b/server/sonar-web/src/main/js/apps/quality-gates/components/Condition.js
@@ -0,0 +1,253 @@
+/*
+ * SonarQube
+ * Copyright (C) 2009-2016 SonarSource SA
+ * mailto:contact AT sonarsource DOT com
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+import React, { Component } from 'react';
+import Select from 'react-select';
+
+import DeleteConditionView from '../views/gate-conditions-delete-view';
+import Checkbox from '../../../components/shared/checkbox';
+import { createCondition, updateCondition } from '../../../api/quality-gates';
+import { translate } from '../../../helpers/l10n';
+import { formatMeasure } from '../../../helpers/measures';
+
+export default class Condition extends Component {
+ constructor (props) {
+ super(props);
+ this.handleChange = this.handleChange.bind(this);
+ this.state = {
+ changed: false,
+ period: props.condition.period,
+ op: props.condition.op
+ };
+ }
+
+ componentDidMount () {
+ const { condition } = this.props;
+
+ if (!condition.id) {
+ this.refs.operator.focus();
+ }
+ }
+
+ handleChange () {
+ this.setState({ changed: true });
+ }
+
+ handleOperatorChange (option) {
+ const { value } = option;
+ this.setState({ changed: true, op: value });
+ }
+
+ handlePeriodChange (checked) {
+ const period = checked ? '1' : undefined;
+ this.setState({ changed: true, period });
+ }
+
+ handleSaveClick (e) {
+ const { qualityGate, condition, onSaveCondition } = this.props;
+ const period = this.state.period;
+ const data = {
+ metric: condition.metric,
+ op: this.state.op,
+ warning: this.refs.warning.value,
+ error: this.refs.error.value
+ };
+
+ if (period) {
+ data.period = period;
+ }
+
+ e.preventDefault();
+ createCondition(qualityGate.id, data).then(newCondition => {
+ this.setState({ changed: false });
+ onSaveCondition(condition, newCondition);
+ });
+ }
+
+ handleUpdateClick (e) {
+ const { condition, onSaveCondition } = this.props;
+ const period = this.state.period;
+ const data = {
+ id: condition.id,
+ metric: condition.metric,
+ op: this.state.op,
+ warning: this.refs.warning.value,
+ error: this.refs.error.value
+ };
+
+ if (period) {
+ data.period = period;
+ }
+
+ e.preventDefault();
+ updateCondition(data).then(newCondition => {
+ this.setState({ changed: false });
+ onSaveCondition(condition, newCondition);
+ });
+ }
+
+ handleDeleteClick (e) {
+ const { qualityGate, condition, metrics, onDeleteCondition } = this.props;
+ const metric = metrics.find(metric => metric.key === condition.metric);
+
+ e.preventDefault();
+ new DeleteConditionView({
+ qualityGate,
+ condition,
+ metric,
+ onDelete: () => onDeleteCondition(condition)
+ }).render();
+ }
+
+ handleCancelClick (e) {
+ const { condition, onDeleteCondition } = this.props;
+
+ e.preventDefault();
+ onDeleteCondition(condition);
+ }
+
+ renderPeriodValue () {
+ const { condition } = this.props;
+ const isLeakSelected = !!this.state.period;
+ const isDiffMetric = condition.metric.indexOf('new_') === 0;
+
+ if (isDiffMetric) {
+ return (
+ <span className="note">
+ {translate('quality_gates.condition.leak.unconditional')}
+ </span>
+ );
+ } else {
+ return isLeakSelected ?
+ translate('quality_gates.condition.leak.yes') :
+ translate('quality_gates.condition.leak.no');
+ }
+ }
+
+ render () {
+ const { condition, edit, metrics } = this.props;
+ const metric = metrics.find(metric => metric.key === condition.metric);
+ const isDiffMetric = condition.metric.indexOf('new_') === 0;
+ const isLeakSelected = !!this.state.period;
+ const operators = ['LT', 'GT', 'EQ', 'NE'];
+ const operatorOptions = operators.map(op => {
+ return {
+ label: translate('quality_gates.operator', op),
+ value: op
+ };
+ });
+
+ return (
+ <tr>
+ <td className="text-middle nowrap">
+ {metric.name}
+ {metric.hidden && (
+ <span className="text-danger little-spacer-left">
+ {translate('deprecated')}
+ </span>
+ )}
+ </td>
+
+ <td className="thin text-middle nowrap">
+ {(edit && !isDiffMetric) ? (
+ <Checkbox
+ initiallyChecked={isLeakSelected}
+ onCheck={this.handlePeriodChange.bind(this)}/>
+ ) : this.renderPeriodValue()}
+ </td>
+
+ <td className="thin text-middle nowrap">
+ {edit ? (
+ <Select
+ ref="operator"
+ className="input-medium"
+ name="operator"
+ value={this.state.op}
+ clearable={false}
+ searchable={false}
+ options={operatorOptions}
+ onChange={this.handleOperatorChange.bind(this)}/>
+ ) : translate('quality_gates.operator', condition.op)}
+ </td>
+
+ <td className="thin text-middle nowrap">
+ {edit ? (
+ <input
+ ref="warning"
+ name="warning"
+ type="text"
+ className="input-tiny text-middle"
+ defaultValue={condition.warning}
+ data-type={metric.type}
+ placeholder={metric.placeholder}
+ onChange={this.handleChange}/>
+ ) : formatMeasure(condition.warning, metric.type)}
+ </td>
+
+ <td className="thin text-middle nowrap">
+ {edit ? (
+ <input
+ ref="error"
+ name="error"
+ type="text"
+ className="input-tiny text-middle"
+ defaultValue={condition.error}
+ data-type={metric.type}
+ onChange={this.handleChange}/>
+ ) : formatMeasure(condition.error, metric.type)}
+ </td>
+
+ {edit && (
+ <td className="thin text-middle nowrap">
+ {condition.id ? (
+ <div className="button-group">
+ <button
+ className="update-condition"
+ disabled={!this.state.changed}
+ onClick={this.handleUpdateClick.bind(this)}>
+ {translate('update_verb')}
+ </button>
+ <button
+ className="button-red delete-condition"
+ onClick={this.handleDeleteClick.bind(this)}>
+ {translate('delete')}
+ </button>
+ </div>
+ ) : (
+ <div className="button-group">
+ <button
+ className="add-condition"
+ onClick={this.handleSaveClick.bind(this)}>
+ {translate('add_verb')}
+ </button>
+ <a
+ className="action cancel-add-condition"
+ href="#"
+ onClick={this.handleCancelClick.bind(this)}>
+ {translate('cancel')}
+ </a>
+ </div>
+ )}
+ </td>
+ )}
+
+ </tr>
+ );
+ }
+}
diff --git a/server/sonar-web/src/main/js/apps/quality-gates/components/Conditions.js b/server/sonar-web/src/main/js/apps/quality-gates/components/Conditions.js
new file mode 100644
index 00000000000..fc7e90f4e88
--- /dev/null
+++ b/server/sonar-web/src/main/js/apps/quality-gates/components/Conditions.js
@@ -0,0 +1,104 @@
+/*
+ * SonarQube
+ * Copyright (C) 2009-2016 SonarSource SA
+ * mailto:contact AT sonarsource DOT com
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+import _ from 'underscore';
+import React from 'react';
+
+import ConditionsAlert from './ConditionsAlert';
+import AddConditionForm from './AddConditionForm';
+import Condition from './Condition';
+import { translate } from '../../../helpers/l10n';
+
+function getKey (condition, index) {
+ return condition.id ? condition.id : `new-${index}`;
+}
+
+export default function Conditions (
+ {
+ qualityGate,
+ conditions,
+ metrics,
+ periods,
+ edit,
+ onAddCondition,
+ onSaveCondition,
+ onDeleteCondition
+ }
+) {
+ const sortedConditions = _.sortBy(conditions, condition => {
+ return metrics.find(metric => metric.key === condition.metric).name;
+ });
+
+
+ return (
+ <div id="quality-gate-conditions" className="quality-gate-section">
+ <h3 className="spacer-bottom">
+ {translate('quality_gates.conditions')}
+ </h3>
+
+ <ConditionsAlert/>
+
+ {sortedConditions.length ? (
+ <table id="quality-gate-conditions" className="data zebra zebra-hover">
+ <thead>
+ <tr>
+ <th className="nowrap">
+ {translate('quality_gates.conditions.metric')}
+ </th>
+ <th className="thin nowrap">
+ {translate('quality_gates.conditions.leak')}
+ </th>
+ <th className="thin nowrap">
+ {translate('quality_gates.conditions.operator')}
+ </th>
+ <th className="thin nowrap">
+ {translate('quality_gates.conditions.warning')}
+ </th>
+ <th className="thin nowrap">
+ {translate('quality_gates.conditions.error')}
+ </th>
+ {edit && <th></th>}
+ </tr>
+ </thead>
+ <tbody>
+ {sortedConditions.map((condition, index) => (
+ <Condition
+ key={getKey(condition, index)}
+ qualityGate={qualityGate}
+ condition={condition}
+ metrics={metrics}
+ periods={periods}
+ edit={edit}
+ onSaveCondition={onSaveCondition}
+ onDeleteCondition={onDeleteCondition}/>
+ ))}
+ </tbody>
+ </table>
+ ) : (
+ <div className="big-spacer-top">
+ {translate('quality_gates.no_conditions')}
+ </div>
+ )}
+
+ {edit && (
+ <AddConditionForm metrics={metrics} onSelect={onAddCondition}/>
+ )}
+ </div>
+ );
+}
diff --git a/server/sonar-web/src/main/js/apps/quality-gates/components/ConditionsAlert.js b/server/sonar-web/src/main/js/apps/quality-gates/components/ConditionsAlert.js
new file mode 100644
index 00000000000..c2f8d3274a5
--- /dev/null
+++ b/server/sonar-web/src/main/js/apps/quality-gates/components/ConditionsAlert.js
@@ -0,0 +1,73 @@
+/*
+ * SonarQube
+ * Copyright (C) 2009-2016 SonarSource SA
+ * mailto:contact AT sonarsource DOT com
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+import React, { Component } from 'react';
+
+import { translate } from '../../../helpers/l10n';
+
+export default class ConditionsAlert extends Component {
+ state = {
+ expanded: false
+ };
+
+ handleMoreClick (e) {
+ e.preventDefault();
+ this.setState({ expanded: true });
+ }
+
+ render () {
+ const { expanded } = this.state;
+
+ return (
+ <div className="big-spacer-bottom">
+ {translate('quality_gates.introduction')}
+ {!expanded && (
+ <a
+ className="spacer-left"
+ href="#"
+ onClick={this.handleMoreClick.bind(this)}>
+ {translate('more')}
+ </a>
+ )}
+ {expanded && (
+ <div className="spacer-top">
+ {translate('quality_gates.health_icons')}
+ <ul>
+ <li className="little-spacer-top">
+ <i className="icon-alert-ok"></i>
+ {' '}
+ {translate('alerts.notes.ok')}
+ </li>
+ <li className="little-spacer-top">
+ <i className="icon-alert-warn"></i>
+ {' '}
+ {translate('alerts.notes.warn')}
+ </li>
+ <li className="little-spacer-top">
+ <i className="icon-alert-error"></i>
+ {' '}
+ {translate('alerts.notes.error')}
+ </li>
+ </ul>
+ </div>
+ )}
+ </div>
+ );
+ }
+}
diff --git a/server/sonar-web/src/main/js/apps/quality-gates/components/Details.js b/server/sonar-web/src/main/js/apps/quality-gates/components/Details.js
new file mode 100644
index 00000000000..7c40c9f1f55
--- /dev/null
+++ b/server/sonar-web/src/main/js/apps/quality-gates/components/Details.js
@@ -0,0 +1,138 @@
+/*
+ * SonarQube
+ * Copyright (C) 2009-2016 SonarSource SA
+ * mailto:contact AT sonarsource DOT com
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+import React, { Component } from 'react';
+
+import { fetchQualityGate, setQualityGateAsDefault, unsetQualityGateAsDefault } from '../../../api/quality-gates';
+import DetailsHeader from './DetailsHeader';
+import DetailsContent from './DetailsContent';
+import RenameView from '../views/rename-view';
+import CopyView from '../views/copy-view';
+import DeleteView from '../views/delete-view';
+
+export default class Details extends Component {
+ componentDidMount () {
+ this.fetchDetails();
+ }
+
+ componentDidUpdate (nextProps) {
+ if (nextProps.params.id !== this.props.params.id) {
+ this.fetchDetails();
+ }
+ }
+
+ fetchDetails () {
+ const { id } = this.props.params;
+ const { onShow } = this.props;
+
+ fetchQualityGate(id).then(qualityGate => {
+ onShow(qualityGate);
+ });
+ }
+
+ handleRenameClick () {
+ const { qualityGate, onRename } = this.props;
+
+ new RenameView({
+ qualityGate,
+ onRename: (qualityGate, newName) => {
+ onRename(qualityGate, newName);
+ }
+ }).render();
+ }
+
+ handleCopyClick () {
+ const { qualityGate, onCopy } = this.props;
+ const { router } = this.context;
+
+ new CopyView({
+ qualityGate,
+ onCopy: (newQualityGate) => {
+ onCopy(newQualityGate);
+ router.push(`/show/${newQualityGate.id}`);
+ }
+ }).render();
+ }
+
+ handleSetAsDefaultClick () {
+ const { qualityGate, onSetAsDefault, onUnsetAsDefault } = this.props;
+
+ if (qualityGate.isDefault) {
+ unsetQualityGateAsDefault(qualityGate.id)
+ .then(() => onUnsetAsDefault(qualityGate));
+ } else {
+ setQualityGateAsDefault(qualityGate.id)
+ .then(() => onSetAsDefault(qualityGate));
+ }
+ }
+
+ handleDeleteClick () {
+ const { qualityGate, onDelete } = this.props;
+ const { router } = this.context;
+
+ new DeleteView({
+ qualityGate,
+ onDelete: (qualityGate) => {
+ onDelete(qualityGate);
+ router.replace('/');
+ }
+ }).render();
+ }
+
+ render () {
+ const { qualityGate, edit, metrics, periods } = this.props;
+ const { onAddCondition, onDeleteCondition, onSaveCondition } = this.props;
+
+ if (!qualityGate) {
+ return (
+ <div className="search-navigator-workspace">
+ <div className="search-navigator-workspace-header" style={{ top: 30 }}>
+ <h2 className="search-navigator-header-component">&nbsp;</h2>
+ </div>
+ <div className="search-navigator-workspace-details"></div>
+ </div>
+ );
+ }
+
+ return (
+ <div className="search-navigator-workspace">
+ <DetailsHeader
+ qualityGate={qualityGate}
+ edit={edit}
+ onRename={this.handleRenameClick.bind(this)}
+ onCopy={this.handleCopyClick.bind(this)}
+ onSetAsDefault={this.handleSetAsDefaultClick.bind(this)}
+ onDelete={this.handleDeleteClick.bind(this)}/>
+
+ <DetailsContent
+ gate={qualityGate}
+ canEdit={edit}
+ metrics={metrics}
+ periods={periods}
+ onAddCondition={onAddCondition}
+ onSaveCondition={onSaveCondition}
+ onDeleteCondition={onDeleteCondition}/>
+ </div>
+ );
+ }
+}
+
+Details.contextTypes = {
+ router: React.PropTypes.object.isRequired
+};
diff --git a/server/sonar-web/src/main/js/apps/quality-gates/components/DetailsContent.js b/server/sonar-web/src/main/js/apps/quality-gates/components/DetailsContent.js
new file mode 100644
index 00000000000..f53cfb36ae1
--- /dev/null
+++ b/server/sonar-web/src/main/js/apps/quality-gates/components/DetailsContent.js
@@ -0,0 +1,62 @@
+/*
+ * SonarQube
+ * Copyright (C) 2009-2016 SonarSource SA
+ * mailto:contact AT sonarsource DOT com
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+import React, { Component } from 'react';
+
+import Conditions from './Conditions';
+import Projects from './Projects';
+import { translate } from '../../../helpers/l10n';
+
+export default class DetailsContent extends Component {
+ render () {
+ const { gate, canEdit, metrics, periods } = this.props;
+ const { onAddCondition, onDeleteCondition, onSaveCondition } = this.props;
+ const conditions = gate.conditions || [];
+
+ return (
+ <div
+ ref="container"
+ className="search-navigator-workspace-details">
+ <Conditions
+ qualityGate={gate}
+ conditions={conditions}
+ metrics={metrics}
+ periods={periods}
+ edit={canEdit}
+ onAddCondition={onAddCondition}
+ onSaveCondition={onSaveCondition}
+ onDeleteCondition={onDeleteCondition}/>
+
+ <div id="quality-gate-projects" className="quality-gate-section">
+ <h3 className="spacer-bottom">
+ {translate('quality_gates.projects')}
+ </h3>
+ {gate.isDefault ? (
+ canEdit ? translate('quality_gates.projects_for_default.edit') :
+ translate('quality_gates.projects_for_default')
+ ) : (
+ <Projects
+ qualityGate={gate}
+ edit={canEdit}/>
+ )}
+ </div>
+ </div>
+ );
+ }
+}
diff --git a/server/sonar-web/src/main/js/apps/quality-gates/components/DetailsHeader.js b/server/sonar-web/src/main/js/apps/quality-gates/components/DetailsHeader.js
new file mode 100644
index 00000000000..fc9675d28d8
--- /dev/null
+++ b/server/sonar-web/src/main/js/apps/quality-gates/components/DetailsHeader.js
@@ -0,0 +1,77 @@
+/*
+ * SonarQube
+ * Copyright (C) 2009-2016 SonarSource SA
+ * mailto:contact AT sonarsource DOT com
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+import React from 'react';
+
+import { translate } from '../../../helpers/l10n';
+
+export default function DetailsHeader ({ qualityGate, edit, onRename, onCopy, onSetAsDefault, onDelete }) {
+ function handleRenameClick (e) {
+ e.preventDefault();
+ onRename();
+ }
+
+ function handleCopyClick (e) {
+ e.preventDefault();
+ onCopy();
+ }
+
+ function handleSetAsDefaultClick (e) {
+ e.preventDefault();
+ onSetAsDefault();
+ }
+
+ function handleDeleteClick (e) {
+ e.preventDefault();
+ onDelete();
+ }
+
+ return (
+ <div className="search-navigator-workspace-header" style={{ top: 30 }}>
+ <h2 className="search-navigator-header-component">{qualityGate.name}</h2>
+ {edit && (
+ <div className="search-navigator-header-actions">
+ <div className="button-group">
+ <button
+ id="quality-gate-rename"
+ onClick={handleRenameClick}>
+ {translate('rename')}
+ </button>
+ <button
+ id="quality-gate-copy"
+ onClick={handleCopyClick}>
+ {translate('copy')}
+ </button>
+ <button
+ id="quality-gate-toggle-default"
+ onClick={handleSetAsDefaultClick}>
+ {qualityGate.isDefault ? translate('unset_as_default') : translate('set_as_default')}
+ </button>
+ <button
+ id="quality-gate-delete"
+ className="button-red"
+ onClick={handleDeleteClick}>
+ {translate('delete')}
+ </button>
+ </div>
+ </div>
+ )}
+ </div>
+ );
+}
diff --git a/server/sonar-web/src/main/js/apps/quality-gates/router.js b/server/sonar-web/src/main/js/apps/quality-gates/components/Intro.js
index bfe973a5811..dbb43fbde68 100644
--- a/server/sonar-web/src/main/js/apps/quality-gates/router.js
+++ b/server/sonar-web/src/main/js/apps/quality-gates/components/Intro.js
@@ -17,25 +17,17 @@
* along with this program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
-import Backbone from 'backbone';
-
-export default Backbone.Router.extend({
- routes: {
- '': 'index',
- 'show/:id': 'show'
- },
-
- initialize (options) {
- this.app = options.app;
- },
-
- index () {
- this.app.controller.index();
- },
-
- show (id) {
- this.app.controller.show(id);
- }
-});
+import React from 'react';
+import { translate } from '../../../helpers/l10n';
+export default function Intro () {
+ return (
+ <div className="search-navigator-workspace">
+ <div className="search-navigator-intro markdown">
+ <p>{translate('quality_gates.intro.1')}</p>
+ <p>{translate('quality_gates.intro.2')}</p>
+ </div>
+ </div>
+ );
+}
diff --git a/server/sonar-web/src/main/js/apps/quality-gates/components/List.js b/server/sonar-web/src/main/js/apps/quality-gates/components/List.js
new file mode 100644
index 00000000000..88a9a16f107
--- /dev/null
+++ b/server/sonar-web/src/main/js/apps/quality-gates/components/List.js
@@ -0,0 +1,53 @@
+/*
+ * SonarQube
+ * Copyright (C) 2009-2016 SonarSource SA
+ * mailto:contact AT sonarsource DOT com
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+import React from 'react';
+import { Link } from 'react-router';
+
+import { translate } from '../../../helpers/l10n';
+
+export default function List ({ qualityGates }) {
+ return (
+ <div className="list-group">
+ {qualityGates.map(qualityGate => (
+ <Link
+ key={qualityGate.id}
+ to={`/show/${qualityGate.id}`}
+ activeClassName="active"
+ className="list-group-item"
+ data-id={qualityGate.id}>
+ <table>
+ <tbody>
+ <tr>
+ <td className="text-top">
+ {qualityGate.name}
+ </td>
+ <td className="text-top thin nowrap spacer-left">
+ {qualityGate.isDefault && (
+ <span className="badge pull-right">{translate('default')}</span>
+ )}
+ </td>
+ </tr>
+ </tbody>
+ </table>
+ </Link>
+ ))}
+ </div>
+ );
+}
diff --git a/server/sonar-web/src/main/js/apps/quality-gates/gate-view.js b/server/sonar-web/src/main/js/apps/quality-gates/components/ListHeader.js
index 85f709cc324..0ecd1fc56da 100644
--- a/server/sonar-web/src/main/js/apps/quality-gates/gate-view.js
+++ b/server/sonar-web/src/main/js/apps/quality-gates/components/ListHeader.js
@@ -17,31 +17,29 @@
* along with this program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
-import Marionette from 'backbone.marionette';
-import Template from './templates/quality-gates-gate.hbs';
+import React from 'react';
-export default Marionette.ItemView.extend({
- tagName: 'a',
- className: 'list-group-item',
- template: Template,
+import CreateView from '../views/create-view';
+import { translate } from '../../../helpers/l10n';
- modelEvents: {
- 'change': 'render'
- },
-
- events: {
- 'click': 'onClick'
- },
-
- onRender () {
- this.$el.toggleClass('active', this.options.highlighted);
- this.$el.attr('data-id', this.model.id);
- },
-
- onClick (e) {
+export default function ListHeader ({ canEdit, onAdd }) {
+ function handleAddClick (e) {
e.preventDefault();
- this.model.trigger('select', this.model);
+ new CreateView({ onAdd }).render();
}
-});
-
+ return (
+ <div>
+ <h1 className="page-title">{translate('quality_gates.page')}</h1>
+ {canEdit && (
+ <div className="page-actions">
+ <div className="button-group">
+ <button id="quality-gate-add" onClick={handleAddClick}>
+ {translate('create')}
+ </button>
+ </div>
+ </div>
+ )}
+ </div>
+ );
+}
diff --git a/server/sonar-web/src/main/js/apps/quality-gates/gates.js b/server/sonar-web/src/main/js/apps/quality-gates/components/Projects.js
index df7d2493c08..62b974bf76d 100644
--- a/server/sonar-web/src/main/js/apps/quality-gates/gates.js
+++ b/server/sonar-web/src/main/js/apps/quality-gates/components/Projects.js
@@ -17,33 +17,47 @@
* along with this program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
-import _ from 'underscore';
-import Backbone from 'backbone';
-import Gate from './gate';
+import React, { Component } from 'react';
-export default Backbone.Collection.extend({
- model: Gate,
+import ProjectsView from '../views/gate-projects-view';
- url () {
- return '/api/qualitygates/list';
- },
+export default class Projects extends Component {
+ componentDidMount () {
+ this.renderView();
+ }
- parse (r) {
- return r.qualitygates.map(function (gate) {
- return _.extend(gate, { isDefault: gate.id === r.default });
- });
- },
+ componentWillUpdate () {
+ this.destroyView();
+ }
- comparator (item) {
- return item.get('name').toLowerCase();
- },
+ componentDidUpdate () {
+ this.renderView();
+ }
- toggleDefault (gate) {
- const isDefault = gate.isDefault();
- this.forEach(function (model) {
- model.set({ isDefault: gate.id === model.id ? !isDefault : false });
+ componentWillUnmount () {
+ this.destroyView();
+ }
+
+ renderView () {
+ const { qualityGate, edit } = this.props;
+
+ this.projectsView = new ProjectsView({
+ qualityGate,
+ edit,
+ container: this.refs.container
});
+ this.projectsView.render();
}
-});
+ destroyView () {
+ if (this.projectsView) {
+ this.projectsView.destroy();
+ }
+ }
+ render () {
+ return (
+ <div ref="container"></div>
+ );
+ }
+}
diff --git a/server/sonar-web/src/main/js/apps/quality-gates/components/QualityGatesApp.js b/server/sonar-web/src/main/js/apps/quality-gates/components/QualityGatesApp.js
new file mode 100644
index 00000000000..8d3d4838a6c
--- /dev/null
+++ b/server/sonar-web/src/main/js/apps/quality-gates/components/QualityGatesApp.js
@@ -0,0 +1,81 @@
+/*
+ * SonarQube
+ * Copyright (C) 2009-2016 SonarSource SA
+ * mailto:contact AT sonarsource DOT com
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+import React, { Component } from 'react';
+
+import ListHeader from './ListHeader';
+import List from './List';
+
+import {
+ fetchQualityGatesAppDetails,
+ fetchQualityGates as fetchQualityGatesAPI
+} from '../../../api/quality-gates';
+
+export default class QualityGatesApp extends Component {
+ state = {};
+
+ componentDidMount () {
+ this.fetchQualityGates();
+ }
+
+ fetchQualityGates () {
+ Promise.all([
+ fetchQualityGatesAppDetails(),
+ fetchQualityGatesAPI()
+ ]).then(responses => {
+ const [details, qualityGates] = responses;
+ const { updateStore } = this.props;
+
+ updateStore({ ...details, qualityGates });
+ });
+ }
+
+ handleAdd (qualityGate) {
+ const { addQualityGate } = this.props;
+ const { router } = this.context;
+
+ addQualityGate(qualityGate);
+ router.push(`/show/${qualityGate.id}`);
+ }
+
+ render () {
+ const { children, qualityGates, edit } = this.props;
+
+ return (
+ <div className="search-navigator sticky search-navigator-extended-view">
+ <div className="search-navigator-side search-navigator-side-light" style={{ top: 30 }}>
+ <div className="search-navigator-filters">
+ <ListHeader
+ canEdit={edit}
+ onAdd={this.handleAdd.bind(this)}/>
+ </div>
+ <div className="quality-gates-results panel">
+ {qualityGates && <List qualityGates={qualityGates}/>}
+ </div>
+ </div>
+
+ {!!qualityGates && children}
+ </div>
+ );
+ }
+}
+
+QualityGatesApp.contextTypes = {
+ router: React.PropTypes.object.isRequired
+};
diff --git a/server/sonar-web/src/main/js/apps/quality-gates/condition.js b/server/sonar-web/src/main/js/apps/quality-gates/condition.js
deleted file mode 100644
index 65bce51a82b..00000000000
--- a/server/sonar-web/src/main/js/apps/quality-gates/condition.js
+++ /dev/null
@@ -1,66 +0,0 @@
-/*
- * SonarQube
- * Copyright (C) 2009-2016 SonarSource SA
- * mailto:contact AT sonarsource DOT com
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation; either
- * version 3 of the License, or (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public License
- * along with this program; if not, write to the Free Software Foundation,
- * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
- */
-import Backbone from 'backbone';
-
-export default Backbone.Model.extend({
-
- defaults: {
- period: 0
- },
-
- url () {
- return '/api/qualitygates';
- },
-
- createUrl () {
- return this.url() + '/create_condition';
- },
-
- updateUrl () {
- return this.url() + '/update_condition';
- },
-
- deleteUrl () {
- return this.url() + '/delete_condition';
- },
-
- sync (method, model, options) {
- const opts = options || {};
- opts.type = 'POST';
- if (method === 'create') {
- opts.url = this.createUrl();
- opts.data = model.toJSON();
- }
- if (method === 'update') {
- opts.url = this.updateUrl();
- opts.data = model.toJSON();
- }
- if (method === 'delete') {
- opts.url = this.deleteUrl();
- opts.data = { id: model.id };
- }
- if (opts.data.period === '0') {
- delete opts.data.period;
- }
- return Backbone.ajax(opts);
- }
-});
-
-
diff --git a/server/sonar-web/src/main/js/apps/quality-gates/conditions.js b/server/sonar-web/src/main/js/apps/quality-gates/conditions.js
deleted file mode 100644
index ae0569b966f..00000000000
--- a/server/sonar-web/src/main/js/apps/quality-gates/conditions.js
+++ /dev/null
@@ -1,27 +0,0 @@
-/*
- * SonarQube
- * Copyright (C) 2009-2016 SonarSource SA
- * mailto:contact AT sonarsource DOT com
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation; either
- * version 3 of the License, or (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public License
- * along with this program; if not, write to the Free Software Foundation,
- * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
- */
-import Backbone from 'backbone';
-import Condition from './condition';
-
-export default Backbone.Collection.extend({
- model: Condition
-});
-
-
diff --git a/server/sonar-web/src/main/js/apps/quality-gates/containers/DetailsContainer.js b/server/sonar-web/src/main/js/apps/quality-gates/containers/DetailsContainer.js
new file mode 100644
index 00000000000..28a8dd342b9
--- /dev/null
+++ b/server/sonar-web/src/main/js/apps/quality-gates/containers/DetailsContainer.js
@@ -0,0 +1,56 @@
+/*
+ * SonarQube
+ * Copyright (C) 2009-2016 SonarSource SA
+ * mailto:contact AT sonarsource DOT com
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+import { connect } from 'react-redux';
+
+import {
+ deleteQualityGate,
+ showQualityGate,
+ renameQualityGate,
+ copyQualityGate,
+ setQualityGateAsDefault,
+ unsetQualityGateAsDefault,
+ addCondition,
+ deleteCondition,
+ saveCondition
+} from '../store/actions';
+import Details from '../components/Details';
+
+function mapStateToProps (state) {
+ return state.rootReducer;
+}
+
+function mapDispatchToProps (dispatch) {
+ return {
+ onShow: (qualityGate) => dispatch(showQualityGate(qualityGate)),
+ onDelete: (qualityGate) => dispatch(deleteQualityGate(qualityGate)),
+ onRename: (qualityGate, newName) => dispatch(renameQualityGate(qualityGate, newName)),
+ onCopy: (qualityGate) => dispatch(copyQualityGate(qualityGate)),
+ onSetAsDefault: (qualityGate) => dispatch(setQualityGateAsDefault(qualityGate)),
+ onUnsetAsDefault: (qualityGate) => dispatch(unsetQualityGateAsDefault(qualityGate)),
+ onAddCondition: (metric) => dispatch(addCondition(metric)),
+ onSaveCondition: (oldCondition, newCondition) => dispatch(saveCondition(oldCondition, newCondition)),
+ onDeleteCondition: (condition) => dispatch(deleteCondition(condition))
+ };
+}
+
+export default connect(
+ mapStateToProps,
+ mapDispatchToProps
+)(Details);
diff --git a/server/sonar-web/src/main/js/apps/quality-gates/gates-view.js b/server/sonar-web/src/main/js/apps/quality-gates/containers/QualityGatesAppContainer.js
index 0df6623ff16..d916ce1dd0a 100644
--- a/server/sonar-web/src/main/js/apps/quality-gates/gates-view.js
+++ b/server/sonar-web/src/main/js/apps/quality-gates/containers/QualityGatesAppContainer.js
@@ -17,27 +17,24 @@
* along with this program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
-import Marionette from 'backbone.marionette';
-import ItemView from './gate-view';
-import Template from './templates/quality-gates-gates.hbs';
+import { connect } from 'react-redux';
-export default Marionette.CompositeView.extend({
- className: 'list-group',
- template: Template,
- childView: ItemView,
- childViewContainer: '.js-list',
+import { setState, addQualityGate, deleteQualityGate } from '../store/actions';
+import QualityGateApp from '../components/QualityGatesApp';
- childViewOptions (model) {
- return {
- collectionView: this,
- highlighted: model.id === this.highlighted
- };
- },
-
- highlight (id) {
- this.highlighted = id;
- this.render();
- }
-});
+function mapStateToProps (state) {
+ return state.rootReducer;
+}
+function mapDispatchToProps (dispatch) {
+ return {
+ updateStore: (nextState) => dispatch(setState(nextState)),
+ addQualityGate: (qualityGate) => dispatch(addQualityGate(qualityGate)),
+ deleteQualityGate: (qualityGate) => dispatch(deleteQualityGate(qualityGate))
+ };
+}
+export default connect(
+ mapStateToProps,
+ mapDispatchToProps
+)(QualityGateApp);
diff --git a/server/sonar-web/src/main/js/apps/quality-gates/controller.js b/server/sonar-web/src/main/js/apps/quality-gates/controller.js
deleted file mode 100644
index 826b5d57cca..00000000000
--- a/server/sonar-web/src/main/js/apps/quality-gates/controller.js
+++ /dev/null
@@ -1,83 +0,0 @@
-/*
- * SonarQube
- * Copyright (C) 2009-2016 SonarSource SA
- * mailto:contact AT sonarsource DOT com
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation; either
- * version 3 of the License, or (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public License
- * along with this program; if not, write to the Free Software Foundation,
- * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
- */
-import _ from 'underscore';
-import Marionette from 'backbone.marionette';
-import DetailsView from './details-view';
-import HeaderView from './header-view';
-
-export default Marionette.Controller.extend({
-
- initialize (options) {
- this.app = options.app;
- this.canEdit = this.app.canEdit;
- this.listenTo(this.app.gates, 'select', this.onSelect);
- this.listenTo(this.app.gates, 'destroy', this.onDestroy);
- },
-
- index () {
- this.app.gates.fetch();
- },
-
- show (id) {
- const that = this;
- this.app.gates.fetch().done(function () {
- const gate = that.app.gates.get(id);
- if (gate != null) {
- gate.trigger('select', gate, { trigger: false });
- }
- });
- },
-
- onSelect (gate, options) {
- const that = this;
- const route = 'show/' + gate.id;
- const opts = _.defaults(options || {}, { trigger: true });
- if (opts.trigger) {
- this.app.router.navigate(route);
- }
- this.app.gatesView.highlight(gate.id);
- gate.fetch().done(function () {
- const headerView = new HeaderView({
- model: gate,
- canEdit: that.canEdit
- });
- that.app.layout.headerRegion.show(headerView);
-
- const detailsView = new DetailsView({
- model: gate,
- canEdit: that.canEdit,
- metrics: that.app.metrics,
- periods: that.app.periods
- });
- that.app.layout.detailsRegion.show(detailsView);
- });
- },
-
- onDestroy () {
- this.app.router.navigate('');
- this.app.layout.headerRegion.reset();
- this.app.layout.detailsRegion.reset();
- this.app.layout.renderIntro();
- this.app.gatesView.highlight(null);
- }
-
-});
-
-
diff --git a/server/sonar-web/src/main/js/apps/quality-gates/copy-view.js b/server/sonar-web/src/main/js/apps/quality-gates/copy-view.js
deleted file mode 100644
index 638ca97eacc..00000000000
--- a/server/sonar-web/src/main/js/apps/quality-gates/copy-view.js
+++ /dev/null
@@ -1,41 +0,0 @@
-/*
- * SonarQube
- * Copyright (C) 2009-2016 SonarSource SA
- * mailto:contact AT sonarsource DOT com
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation; either
- * version 3 of the License, or (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public License
- * along with this program; if not, write to the Free Software Foundation,
- * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
- */
-import FormView from './form-view';
-
-export default FormView.extend({
- method: 'copy',
-
- prepareRequest () {
- const that = this;
- const url = '/api/qualitygates/copy';
- const name = this.$('#quality-gate-form-name').val();
- const options = {
- url,
- data: { id: this.model.id, name }
- };
- return this.sendRequest(options)
- .done(function (r) {
- const gate = that.addGate(r);
- gate.trigger('select', gate);
- });
- }
-});
-
-
diff --git a/server/sonar-web/src/main/js/apps/quality-gates/details-view.js b/server/sonar-web/src/main/js/apps/quality-gates/details-view.js
deleted file mode 100644
index ae49ae0bb9f..00000000000
--- a/server/sonar-web/src/main/js/apps/quality-gates/details-view.js
+++ /dev/null
@@ -1,72 +0,0 @@
-/*
- * SonarQube
- * Copyright (C) 2009-2016 SonarSource SA
- * mailto:contact AT sonarsource DOT com
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation; either
- * version 3 of the License, or (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public License
- * along with this program; if not, write to the Free Software Foundation,
- * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
- */
-import _ from 'underscore';
-import Marionette from 'backbone.marionette';
-import Conditions from './conditions';
-import DetailConditionsView from './gate-conditions-view';
-import ProjectsView from './gate-projects-view';
-import Template from './templates/quality-gate-detail.hbs';
-
-export default Marionette.LayoutView.extend({
- template: Template,
-
- regions: {
- conditionsRegion: '#quality-gate-conditions',
- projectsRegion: '#quality-gate-projects'
- },
-
- modelEvents: {
- 'change': 'render'
- },
-
- onRender () {
- this.showConditions();
- this.showProjects();
- },
-
- orderByName (conditions) {
- const metrics = this.options.metrics;
- return _.sortBy(conditions, (condition) => {
- return _.findWhere(metrics, { key: condition.metric }).name;
- });
- },
-
- showConditions () {
- const conditions = new Conditions(this.orderByName(this.model.get('conditions')));
- const view = new DetailConditionsView({
- canEdit: this.options.canEdit,
- collection: conditions,
- model: this.model,
- metrics: this.options.metrics,
- periods: this.options.periods
- });
- this.conditionsRegion.show(view);
- },
-
- showProjects () {
- const view = new ProjectsView({
- canEdit: this.options.canEdit,
- model: this.model
- });
- this.projectsRegion.show(view);
- }
-});
-
-
diff --git a/server/sonar-web/src/main/js/apps/quality-gates/form-view.js b/server/sonar-web/src/main/js/apps/quality-gates/form-view.js
deleted file mode 100644
index d82e605c7b0..00000000000
--- a/server/sonar-web/src/main/js/apps/quality-gates/form-view.js
+++ /dev/null
@@ -1,66 +0,0 @@
-/*
- * SonarQube
- * Copyright (C) 2009-2016 SonarSource SA
- * mailto:contact AT sonarsource DOT com
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation; either
- * version 3 of the License, or (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public License
- * along with this program; if not, write to the Free Software Foundation,
- * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
- */
-import _ from 'underscore';
-import Backbone from 'backbone';
-import ModalForm from '../../components/common/modal-form';
-import Gate from './gate';
-import Template from './templates/quality-gate-form.hbs';
-
-export default ModalForm.extend({
- template: Template,
-
- onFormSubmit () {
- ModalForm.prototype.onFormSubmit.apply(this, arguments);
- this.disableForm();
- this.prepareRequest();
- },
-
- sendRequest (options) {
- const that = this;
- const opts = _.defaults(options || {}, {
- type: 'POST',
- statusCode: {
- // do not show global error
- 400: null
- }
- });
- return Backbone.ajax(opts)
- .done(function () {
- that.destroy();
- }).fail(function (jqXHR) {
- that.enableForm();
- that.showErrors(jqXHR.responseJSON.errors, jqXHR.responseJSON.warnings);
- });
- },
-
- addGate (attrs) {
- const gate = new Gate(attrs);
- this.collection.add(gate, { merge: true });
- return gate;
- },
-
- serializeData () {
- return _.extend(ModalForm.prototype.serializeData.apply(this, arguments), {
- method: this.method
- });
- }
-});
-
-
diff --git a/server/sonar-web/src/main/js/apps/quality-gates/gate-condition-view.js b/server/sonar-web/src/main/js/apps/quality-gates/gate-condition-view.js
deleted file mode 100644
index dab1a79e834..00000000000
--- a/server/sonar-web/src/main/js/apps/quality-gates/gate-condition-view.js
+++ /dev/null
@@ -1,118 +0,0 @@
-/*
- * SonarQube
- * Copyright (C) 2009-2016 SonarSource SA
- * mailto:contact AT sonarsource DOT com
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation; either
- * version 3 of the License, or (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public License
- * along with this program; if not, write to the Free Software Foundation,
- * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
- */
-import _ from 'underscore';
-import Marionette from 'backbone.marionette';
-import DeleteConditionView from './gate-conditions-delete-view';
-import Template from './templates/quality-gate-detail-condition.hbs';
-import { translate } from '../../helpers/l10n';
-
-export default Marionette.ItemView.extend({
- tagName: 'tr',
- template: Template,
-
- modelEvents: {
- 'change': 'render'
- },
-
- ui: {
- periodSelect: '[name=period]',
- operatorSelect: '[name=operator]',
- warningInput: '[name=warning]',
- errorInput: '[name=error]',
- actionsBox: '.quality-gate-condition-actions',
- updateButton: '.update-condition',
- deleteButton: '.delete-condition'
- },
-
- events: {
- 'click @ui.updateButton': 'saveCondition',
- 'click @ui.deleteButton': 'deleteCondition',
- 'click .add-condition': 'saveCondition',
- 'click .cancel-add-condition': 'cancelAddCondition',
- 'keyup :input': 'enableUpdate',
- 'change :input': 'enableUpdate'
- },
-
- onRender () {
- this.ui.warningInput.val(this.model.get('warning'));
- this.ui.errorInput.val(this.model.get('error'));
-
- this.ui.periodSelect.select2({
- allowClear: false,
- minimumResultsForSearch: 999
- });
-
- this.ui.operatorSelect.select2({
- allowClear: false,
- minimumResultsForSearch: 999
- });
-
- if (this.model.isNew()) {
- this.ui.periodSelect.select2('open');
- }
- },
-
- saveCondition () {
- const attrs = {
- gateId: this.model.isNew() ? this.options.gate.id : void 0,
- period: this.ui.periodSelect.val(),
- op: this.ui.operatorSelect.val(),
- warning: this.ui.warningInput.val(),
- error: this.ui.errorInput.val()
- };
- this.model.save(attrs, { wait: true });
- },
-
- deleteCondition () {
- new DeleteConditionView({
- model: this.model,
- metric: this.getMetric()
- }).render();
- },
-
- cancelAddCondition () {
- this.destroy();
- },
-
- enableUpdate () {
- this.ui.updateButton.prop('disabled', false);
- },
-
- getMetric () {
- const key = this.model.get('metric');
- return _.findWhere(this.options.metrics, { key });
- },
-
- isDiffMetric () {
- const key = this.model.get('metric');
- return key.indexOf('new_') === 0;
- },
-
- serializeData () {
- const period = _.findWhere(this.options.periods, { key: this.model.get('period') });
- return _.extend(Marionette.ItemView.prototype.serializeData.apply(this, arguments), {
- canEdit: this.options.canEdit,
- periods: this.options.periods,
- periodText: period ? period.text : translate('value'),
- metric: this.getMetric(),
- isDiffMetric: this.isDiffMetric()
- });
- }
-});
diff --git a/server/sonar-web/src/main/js/apps/quality-gates/gate-conditions-empty-view.js b/server/sonar-web/src/main/js/apps/quality-gates/gate-conditions-empty-view.js
deleted file mode 100644
index ee1e772273c..00000000000
--- a/server/sonar-web/src/main/js/apps/quality-gates/gate-conditions-empty-view.js
+++ /dev/null
@@ -1,35 +0,0 @@
-/*
- * SonarQube
- * Copyright (C) 2009-2016 SonarSource SA
- * mailto:contact AT sonarsource DOT com
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation; either
- * version 3 of the License, or (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public License
- * along with this program; if not, write to the Free Software Foundation,
- * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
- */
-import _ from 'underscore';
-import Marionette from 'backbone.marionette';
-import Template from './templates/quality-gate-detail-conditions-empty.hbs';
-
-export default Marionette.ItemView.extend({
- tagName: 'tr',
- template: Template,
-
- serializeData () {
- return _.extend(Marionette.ItemView.prototype.serializeData.apply(this, arguments), {
- canEdit: this.options.canEdit
- });
- }
-});
-
-
diff --git a/server/sonar-web/src/main/js/apps/quality-gates/gate-conditions-view.js b/server/sonar-web/src/main/js/apps/quality-gates/gate-conditions-view.js
deleted file mode 100644
index 5d2396b3453..00000000000
--- a/server/sonar-web/src/main/js/apps/quality-gates/gate-conditions-view.js
+++ /dev/null
@@ -1,93 +0,0 @@
-/*
- * SonarQube
- * Copyright (C) 2009-2016 SonarSource SA
- * mailto:contact AT sonarsource DOT com
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation; either
- * version 3 of the License, or (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public License
- * along with this program; if not, write to the Free Software Foundation,
- * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
- */
-import _ from 'underscore';
-import Marionette from 'backbone.marionette';
-import Condition from './condition';
-import ConditionView from './gate-condition-view';
-import ConditionsEmptyView from './gate-conditions-empty-view';
-import Template from './templates/quality-gate-detail-conditions.hbs';
-import { translate } from '../../helpers/l10n';
-
-export default Marionette.CompositeView.extend({
- template: Template,
- childView: ConditionView,
- emptyView: ConditionsEmptyView,
- childViewContainer: '.js-conditions',
-
- ui: {
- metricSelect: '#quality-gate-new-condition-metric'
- },
-
- events: {
- 'click .js-show-more': 'showMoreIntroduction',
- 'change @ui.metricSelect': 'addCondition'
- },
-
- childViewOptions () {
- return {
- canEdit: this.options.canEdit,
- gate: this.model,
- collectionView: this,
- metrics: this.options.metrics,
- periods: this.options.periods
- };
- },
-
- onRender () {
- this.ui.metricSelect.select2({
- allowClear: false,
- width: '250px',
- placeholder: translate('alerts.select_metric')
- });
- },
-
- showMoreIntroduction () {
- this.$('.js-show-more').addClass('hidden');
- this.$('.js-more').removeClass('hidden');
- },
-
- addCondition () {
- const metric = this.ui.metricSelect.val();
- this.ui.metricSelect.select2('val', '');
- const condition = new Condition({ metric });
- this.collection.add(condition);
- },
-
- groupedMetrics () {
- let metrics = this.options.metrics.filter(function (metric) {
- return !metric.hidden;
- });
- metrics = _.groupBy(metrics, 'domain');
- metrics = _.map(metrics, function (list, domain) {
- return {
- domain,
- metrics: _.sortBy(list, 'short_name')
- };
- });
- return _.sortBy(metrics, 'domain');
- },
-
- serializeData () {
- return _.extend(Marionette.CompositeView.prototype.serializeData.apply(this, arguments), {
- canEdit: this.options.canEdit,
- metricGroups: this.groupedMetrics()
- });
- }
-});
diff --git a/server/sonar-web/src/main/js/apps/quality-gates/gate-projects-view.js b/server/sonar-web/src/main/js/apps/quality-gates/gate-projects-view.js
deleted file mode 100644
index a8a7909351b..00000000000
--- a/server/sonar-web/src/main/js/apps/quality-gates/gate-projects-view.js
+++ /dev/null
@@ -1,66 +0,0 @@
-/*
- * SonarQube
- * Copyright (C) 2009-2016 SonarSource SA
- * mailto:contact AT sonarsource DOT com
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation; either
- * version 3 of the License, or (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public License
- * along with this program; if not, write to the Free Software Foundation,
- * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
- */
-import _ from 'underscore';
-import Marionette from 'backbone.marionette';
-import Template from './templates/quality-gate-detail-projects.hbs';
-import '../../components/SelectList';
-import { translate } from '../../helpers/l10n';
-
-export default Marionette.ItemView.extend({
- template: Template,
-
- onRender () {
- if (!this.model.isDefault()) {
- new window.SelectList({
- el: this.$('#select-list-projects'),
- width: '100%',
- readOnly: !this.options.canEdit,
- focusSearch: false,
- format (item) {
- return item.name;
- },
- searchUrl: '/api/qualitygates/search?gateId=' + this.model.id,
- selectUrl: '/api/qualitygates/select',
- deselectUrl: '/api/qualitygates/deselect',
- extra: {
- gateId: this.model.id
- },
- selectParameter: 'projectId',
- selectParameterValue: 'id',
- labels: {
- selected: translate('quality_gates.projects.with'),
- deselected: translate('quality_gates.projects.without'),
- all: translate('quality_gates.projects.all'),
- noResults: translate('quality_gates.projects.noResults')
- },
- tooltips: {
- select: translate('quality_gates.projects.select_hint'),
- deselect: translate('quality_gates.projects.deselect_hint')
- }
- });
- }
- },
-
- serializeData () {
- return _.extend(Marionette.ItemView.prototype.serializeData.apply(this, arguments), {
- canEdit: this.options.canEdit
- });
- }
-});
diff --git a/server/sonar-web/src/main/js/apps/quality-gates/gate.js b/server/sonar-web/src/main/js/apps/quality-gates/gate.js
deleted file mode 100644
index 00ff3718d44..00000000000
--- a/server/sonar-web/src/main/js/apps/quality-gates/gate.js
+++ /dev/null
@@ -1,73 +0,0 @@
-/*
- * SonarQube
- * Copyright (C) 2009-2016 SonarSource SA
- * mailto:contact AT sonarsource DOT com
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation; either
- * version 3 of the License, or (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public License
- * along with this program; if not, write to the Free Software Foundation,
- * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
- */
-import Backbone from 'backbone';
-
-export default Backbone.Model.extend({
-
- isDefault () {
- return this.get('isDefault');
- },
-
- url () {
- return '/api/qualitygates';
- },
-
- showUrl () {
- return this.url() + '/show';
- },
-
- deleteUrl () {
- return this.url() + '/destroy';
- },
-
- toggleDefaultUrl () {
- const method = this.isDefault() ? 'unset_default' : 'set_as_default';
- return this.url() + '/' + method;
- },
-
- sync (method, model, options) {
- const opts = options || {};
- opts.data = opts.data || {};
- opts.data.id = model.id;
- if (method === 'read') {
- opts.url = this.showUrl();
- }
- if (method === 'delete') {
- opts.url = this.deleteUrl();
- opts.type = 'POST';
- }
- return Backbone.ajax(opts);
- },
-
- toggleDefault () {
- const that = this;
- const opts = {
- type: 'POST',
- url: this.toggleDefaultUrl(),
- data: { id: this.id }
- };
- return Backbone.ajax(opts).done(function () {
- that.collection.toggleDefault(that);
- });
- }
-
-});
-
-
diff --git a/server/sonar-web/src/main/js/apps/quality-gates/header-view.js b/server/sonar-web/src/main/js/apps/quality-gates/header-view.js
deleted file mode 100644
index e01e161c9df..00000000000
--- a/server/sonar-web/src/main/js/apps/quality-gates/header-view.js
+++ /dev/null
@@ -1,71 +0,0 @@
-/*
- * SonarQube
- * Copyright (C) 2009-2016 SonarSource SA
- * mailto:contact AT sonarsource DOT com
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation; either
- * version 3 of the License, or (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public License
- * along with this program; if not, write to the Free Software Foundation,
- * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
- */
-import _ from 'underscore';
-import Marionette from 'backbone.marionette';
-import RenameView from './rename-view';
-import CopyView from './copy-view';
-import DeleteView from './delete-view';
-import Template from './templates/quality-gate-detail-header.hbs';
-
-export default Marionette.ItemView.extend({
- template: Template,
-
- modelEvents: {
- 'change': 'render'
- },
-
- events: {
- 'click #quality-gate-rename': 'renameQualityGate',
- 'click #quality-gate-copy': 'copyQualityGate',
- 'click #quality-gate-delete': 'deleteQualityGate',
- 'click #quality-gate-toggle-default': 'toggleDefault'
- },
-
- renameQualityGate () {
- new RenameView({
- model: this.model
- }).render();
- },
-
- copyQualityGate () {
- new CopyView({
- model: this.model,
- collection: this.model.collection
- }).render();
- },
-
- deleteQualityGate () {
- new DeleteView({
- model: this.model
- }).render();
- },
-
- toggleDefault () {
- this.model.toggleDefault();
- },
-
- serializeData () {
- return _.extend(Marionette.ItemView.prototype.serializeData.apply(this, arguments), {
- canEdit: this.options.canEdit
- });
- }
-});
-
-
diff --git a/server/sonar-web/src/main/js/apps/quality-gates/intro-view.js b/server/sonar-web/src/main/js/apps/quality-gates/intro-view.js
deleted file mode 100644
index 083f83b82ae..00000000000
--- a/server/sonar-web/src/main/js/apps/quality-gates/intro-view.js
+++ /dev/null
@@ -1,27 +0,0 @@
-/*
- * SonarQube
- * Copyright (C) 2009-2016 SonarSource SA
- * mailto:contact AT sonarsource DOT com
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation; either
- * version 3 of the License, or (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public License
- * along with this program; if not, write to the Free Software Foundation,
- * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
- */
-import Marionette from 'backbone.marionette';
-import Template from './templates/quality-gates-intro.hbs';
-
-export default Marionette.ItemView.extend({
- template: Template
-});
-
-
diff --git a/server/sonar-web/src/main/js/apps/quality-gates/layout.js b/server/sonar-web/src/main/js/apps/quality-gates/layout.js
deleted file mode 100644
index 351ca9ae43c..00000000000
--- a/server/sonar-web/src/main/js/apps/quality-gates/layout.js
+++ /dev/null
@@ -1,46 +0,0 @@
-/*
- * SonarQube
- * Copyright (C) 2009-2016 SonarSource SA
- * mailto:contact AT sonarsource DOT com
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation; either
- * version 3 of the License, or (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public License
- * along with this program; if not, write to the Free Software Foundation,
- * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
- */
-import Marionette from 'backbone.marionette';
-import IntroView from './intro-view';
-import Template from './templates/quality-gates-layout.hbs';
-
-export default Marionette.LayoutView.extend({
- template: Template,
-
- regions: {
- headerRegion: '.search-navigator-workspace-header',
- actionsRegion: '.search-navigator-filters',
- resultsRegion: '.quality-gates-results',
- detailsRegion: '.search-navigator-workspace-details'
- },
-
- onRender () {
- const top = this.$('.search-navigator').offset().top;
- this.$('.search-navigator-workspace-header').css({ top });
- this.$('.search-navigator-side').css({ top }).isolatedScroll();
- this.renderIntro();
- },
-
- renderIntro () {
- this.detailsRegion.show(new IntroView());
- }
-});
-
-
diff --git a/server/sonar-web/src/main/js/apps/quality-gates/store/actions.js b/server/sonar-web/src/main/js/apps/quality-gates/store/actions.js
new file mode 100644
index 00000000000..201fb4c4e6b
--- /dev/null
+++ b/server/sonar-web/src/main/js/apps/quality-gates/store/actions.js
@@ -0,0 +1,108 @@
+/*
+ * SonarQube
+ * Copyright (C) 2009-2016 SonarSource SA
+ * mailto:contact AT sonarsource DOT com
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+export const SET_STATE = 'SET_STATE';
+export function setState (nextState) {
+ return {
+ type: SET_STATE,
+ nextState
+ };
+}
+
+export const ADD = 'ADD';
+export function addQualityGate (qualityGate) {
+ return {
+ type: 'ADD',
+ qualityGate
+ };
+}
+
+export const DELETE = 'DELETE';
+export function deleteQualityGate (qualityGate) {
+ return {
+ type: 'DELETE',
+ qualityGate
+ };
+}
+
+export const SHOW = 'SHOW';
+export function showQualityGate (qualityGate) {
+ return {
+ type: 'SHOW',
+ qualityGate
+ };
+}
+
+export const RENAME = 'RENAME';
+export function renameQualityGate (qualityGate, newName) {
+ return {
+ type: 'RENAME',
+ qualityGate,
+ newName
+ };
+}
+
+export const COPY = 'COPY';
+export function copyQualityGate (qualityGate) {
+ return {
+ type: COPY,
+ qualityGate
+ };
+}
+
+export const SET_AS_DEFAULT = 'SET_AS_DEFAULT';
+export function setQualityGateAsDefault (qualityGate) {
+ return {
+ type: SET_AS_DEFAULT,
+ qualityGate
+ };
+}
+
+export const UNSET_AS_DEFAULT = 'UNSET_AS_DEFAULT';
+export function unsetQualityGateAsDefault (qualityGate) {
+ return {
+ type: UNSET_AS_DEFAULT,
+ qualityGate
+ };
+}
+
+export const ADD_CONDITION = 'ADD_CONDITION';
+export function addCondition (metric) {
+ return {
+ type: ADD_CONDITION,
+ metric
+ };
+}
+
+export const SAVE_CONDITION = 'SAVE_CONDITION';
+export function saveCondition (oldCondition, newCondition) {
+ return {
+ type: SAVE_CONDITION,
+ oldCondition,
+ newCondition
+ };
+}
+
+export const DELETE_CONDITION = 'DELETE_CONDITION';
+export function deleteCondition (condition) {
+ return {
+ type: DELETE_CONDITION,
+ condition
+ };
+}
diff --git a/server/sonar-web/src/main/js/apps/quality-gates/store/reducers.js b/server/sonar-web/src/main/js/apps/quality-gates/store/reducers.js
new file mode 100644
index 00000000000..aadba7a2415
--- /dev/null
+++ b/server/sonar-web/src/main/js/apps/quality-gates/store/reducers.js
@@ -0,0 +1,93 @@
+/*
+ * SonarQube
+ * Copyright (C) 2009-2016 SonarSource SA
+ * mailto:contact AT sonarsource DOT com
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+import {
+ SET_STATE,
+ ADD,
+ DELETE,
+ SHOW,
+ RENAME,
+ COPY,
+ SET_AS_DEFAULT,
+ UNSET_AS_DEFAULT,
+ ADD_CONDITION,
+ DELETE_CONDITION,
+ SAVE_CONDITION
+} from './actions';
+import { checkIfDefault, addCondition, deleteCondition, replaceCondition } from './utils';
+
+const initialState = {};
+
+export default function rootReducer (state = initialState, action) {
+ switch (action.type) {
+ case SET_STATE:
+ return { ...state, ...action.nextState };
+ case ADD:
+ case COPY:
+ return { ...state, qualityGates: [...state.qualityGates, action.qualityGate] };
+ case DELETE:
+ return { ...state, qualityGates: state.qualityGates.filter(candidate => candidate.id !== action.qualityGate.id) };
+ case SHOW:
+ return {
+ ...state,
+ qualityGate: { ...action.qualityGate, isDefault: checkIfDefault(action.qualityGate, state.qualityGates) }
+ };
+ case RENAME:
+ return {
+ ...state,
+ qualityGates: state.qualityGates.map(candidate => {
+ return candidate.id === action.qualityGate.id ? { ...candidate, name: action.newName } : candidate;
+ }),
+ qualityGate: { ...state.qualityGate, name: action.newName }
+ };
+ case SET_AS_DEFAULT:
+ return {
+ ...state,
+ qualityGates: state.qualityGates.map(candidate => {
+ return { ...candidate, isDefault: candidate.id === action.qualityGate.id };
+ }),
+ qualityGate: { ...state.qualityGate, isDefault: state.qualityGate.id === action.qualityGate.id }
+ };
+ case UNSET_AS_DEFAULT:
+ return {
+ ...state,
+ qualityGates: state.qualityGates.map(candidate => {
+ return candidate.id === action.qualityGate.id ? { ...candidate, isDefault: false } : candidate;
+ }),
+ qualityGate: { ...state.qualityGate, isDefault: false }
+ };
+ case ADD_CONDITION:
+ return {
+ ...state,
+ qualityGate: addCondition(state.qualityGate, action.metric)
+ };
+ case DELETE_CONDITION:
+ return {
+ ...state,
+ qualityGate: deleteCondition(state.qualityGate, action.condition)
+ };
+ case SAVE_CONDITION:
+ return {
+ ...state,
+ qualityGate: replaceCondition(state.qualityGate, action.oldCondition, action.newCondition)
+ };
+ default:
+ return state;
+ }
+}
diff --git a/server/sonar-web/src/main/js/apps/quality-gates/store/utils.js b/server/sonar-web/src/main/js/apps/quality-gates/store/utils.js
new file mode 100644
index 00000000000..81247f6393b
--- /dev/null
+++ b/server/sonar-web/src/main/js/apps/quality-gates/store/utils.js
@@ -0,0 +1,51 @@
+/*
+ * SonarQube
+ * Copyright (C) 2009-2016 SonarSource SA
+ * mailto:contact AT sonarsource DOT com
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+export function checkIfDefault (qualityGate, list) {
+ const finding = list.find(candidate => candidate.id === qualityGate.id);
+
+ return finding ? finding.isDefault : false;
+}
+
+export function addCondition (qualityGate, metric) {
+ const condition = {
+ metric,
+ op: 'LT',
+ warning: '',
+ error: ''
+ };
+ const conditions = [...qualityGate.conditions, condition];
+
+ return { ...qualityGate, conditions };
+}
+
+export function deleteCondition (qualityGate, condition) {
+ const conditions = qualityGate.conditions
+ .filter(candidate => candidate !== condition);
+
+ return { ...qualityGate, conditions };
+}
+
+export function replaceCondition (qualityGate, oldCondition, newCondition) {
+ const conditions = qualityGate.conditions
+ .map(candidate => {
+ return candidate === oldCondition ? newCondition : candidate;
+ });
+ return { ...qualityGate, conditions };
+}
diff --git a/server/sonar-web/src/main/js/apps/quality-gates/templates/_quality-gate-intro.hbs b/server/sonar-web/src/main/js/apps/quality-gates/templates/_quality-gate-intro.hbs
deleted file mode 100644
index 6f194fb7fbb..00000000000
--- a/server/sonar-web/src/main/js/apps/quality-gates/templates/_quality-gate-intro.hbs
+++ /dev/null
@@ -1,7 +0,0 @@
-<div class="">
- <p class="spacer-bottom">Quality Gates are collections of simple boolean thresholds set on project measures. A project
- must pass each of the thresholds in order to pass the Quality Gate as a whole.</p>
-
- <p>It is possible to set a default Quality Gate, which will be applied to all projects not explicitly assigned to some
- other gate.</p>
-</div>
diff --git a/server/sonar-web/src/main/js/apps/quality-gates/templates/quality-gate-actions.hbs b/server/sonar-web/src/main/js/apps/quality-gates/templates/quality-gate-actions.hbs
deleted file mode 100644
index 6826aa94353..00000000000
--- a/server/sonar-web/src/main/js/apps/quality-gates/templates/quality-gate-actions.hbs
+++ /dev/null
@@ -1,8 +0,0 @@
-<h1 class="page-title">{{t 'quality_gates.page'}}</h1>
-{{#if canEdit}}
- <div class="page-actions">
- <div class="button-group">
- <button id="quality-gate-add">{{t 'create'}}</button>
- </div>
- </div>
-{{/if}}
diff --git a/server/sonar-web/src/main/js/apps/quality-gates/templates/quality-gate-detail-condition.hbs b/server/sonar-web/src/main/js/apps/quality-gates/templates/quality-gate-detail-condition.hbs
deleted file mode 100644
index 206b7ceb283..00000000000
--- a/server/sonar-web/src/main/js/apps/quality-gates/templates/quality-gate-detail-condition.hbs
+++ /dev/null
@@ -1,69 +0,0 @@
-<td class="text-middle nowrap">
- {{metric.name}}
- {{#if metric.hidden}}
- <span class="text-danger little-spacer-left">{{t 'deprecated'}}</span>
- {{/if}}
-</td>
-
-<td class="text-right nowrap">
- {{#if canEdit}}
- <select class="input-super-large" name="period">
- {{#unless isDiffMetric}}
- <option value="0" {{#eq period 0}}selected{{/eq}}>{{t 'value'}}</option>
- {{/unless}}
- {{#each periods}}
- <option value="{{key}}" {{#eq key ../period}}selected{{/eq}}>{{text}}</option>
- {{/each}}
- </select>
- {{else}}
- {{periodText}}
- {{/if}}
-</td>
-
-<td class="thin text-middle nowrap">
- {{#if canEdit}}
- <select class="input-medium" name="operator">
- {{#operators}}
- <option value="{{this}}" {{#eq this ../op}}selected{{/eq}}>{{t 'quality_gates.operator' this}}</option>
- {{/operators}}
- </select>
- {{else}}
- {{t 'quality_gates.operator' op}}
- {{/if}}
-</td>
-
-<td class="thin text-middle nowrap">
- <i class="icon-alert-warn" title="{{t 'quality_gates.warning_tooltip'}}"></i>
- {{#if canEdit}}
- <input name="warning" type="text" class="input-tiny text-middle" data-type="{{metric.type}}"
- placeholder="{{metric.placeholder}}">
- {{else}}
- {{formatMeasure warning metric.type}}
- {{/if}}
-</td>
-
-<td class="thin text-middle nowrap">
- <i class="icon-alert-error" title="{{t 'quality_gates.error_tooltip'}}"></i>
- {{#if canEdit}}
- <input name="error" type="text" class="input-tiny text-middle" data-type="{{metric.type}}"
- placeholder="{{metric.placeholder}}">
- {{else}}
- {{formatMeasure error metric.type}}
- {{/if}}
-</td>
-
-{{#if canEdit}}
- <td class="thin text-middle nowrap">
- {{#if id}}
- <div class="button-group">
- <button class="update-condition" disabled>{{t 'update_verb'}}</button>
- <button class="button-red delete-condition">{{t 'delete'}}</button>
- </div>
- {{else}}
- <div class="button-group">
- <button class="add-condition">{{t 'add_verb'}}</button>
- <a class="action cancel-add-condition">{{t 'cancel'}}</a>
- </div>
- {{/if}}
- </td>
-{{/if}}
diff --git a/server/sonar-web/src/main/js/apps/quality-gates/templates/quality-gate-detail-conditions-empty.hbs b/server/sonar-web/src/main/js/apps/quality-gates/templates/quality-gate-detail-conditions-empty.hbs
deleted file mode 100644
index 80a12b45883..00000000000
--- a/server/sonar-web/src/main/js/apps/quality-gates/templates/quality-gate-detail-conditions-empty.hbs
+++ /dev/null
@@ -1,3 +0,0 @@
-<td colspan="{{#if canEdit}}6{{else}}5{{/if}}">
- {{t 'quality_gates.no_conditions'}}
-</td>
diff --git a/server/sonar-web/src/main/js/apps/quality-gates/templates/quality-gate-detail-conditions.hbs b/server/sonar-web/src/main/js/apps/quality-gates/templates/quality-gate-detail-conditions.hbs
deleted file mode 100644
index 4f5a5126665..00000000000
--- a/server/sonar-web/src/main/js/apps/quality-gates/templates/quality-gate-detail-conditions.hbs
+++ /dev/null
@@ -1,41 +0,0 @@
-<h3 class="spacer-bottom">{{t 'quality_gates.conditions'}}</h3>
-
-<div class="alert alert-info">
- <div>
- {{t 'quality_gates.introduction'}}
- <a class="js-show-more pull-right spacer-left">{{t 'more'}}&nbsp;<i class="icon-dropdown"></i></a>
- </div>
- <div class="js-more spacer-top hidden">
- {{t 'quality_gates.health_icons'}}
- <ul>
- <li class="little-spacer-top">
- <i class="icon-alert-ok"></i> {{t 'alerts.notes.ok'}}
- </li>
- <li class="little-spacer-top">
- <i class="icon-alert-warn"></i> {{t 'alerts.notes.warn'}}
- </li>
- <li class="little-spacer-top">
- <i class="icon-alert-error"></i> {{t 'alerts.notes.error'}}
- </li>
- </ul>
- </div>
-</div>
-
-{{#if canEdit}}
- <form class="big-spacer-top spacer-bottom">
- <label for="quality-gate-new-condition-metric" class="text-middle">{{t 'quality_gates.add_condition'}}</label>
- <select id="quality-gate-new-condition-metric" class="text-middle">
- <option></option>
- {{#each metricGroups}}
- <optgroup label="{{this.domain}}">
- {{#each metrics}}
- <option value="{{key}}">{{name}}</option>{{/each}}
- </optgroup>
- {{/each}}
- </select>
- </form>
-{{/if}}
-
-<table class="data zebra width-100">
- <tbody class="js-conditions"></tbody>
-</table>
diff --git a/server/sonar-web/src/main/js/apps/quality-gates/templates/quality-gate-detail-header.hbs b/server/sonar-web/src/main/js/apps/quality-gates/templates/quality-gate-detail-header.hbs
deleted file mode 100644
index c7fcf9a84d0..00000000000
--- a/server/sonar-web/src/main/js/apps/quality-gates/templates/quality-gate-detail-header.hbs
+++ /dev/null
@@ -1,14 +0,0 @@
-<h2 class="search-navigator-header-component">{{name}}</h2>
-
-{{#if canEdit}}
- <div class="search-navigator-header-actions">
- <div class="button-group">
- <button id="quality-gate-rename">{{t 'rename'}}</button>
- <button id="quality-gate-copy">{{t 'copy'}}</button>
- <button id="quality-gate-toggle-default">
- {{#if isDefault}}{{t 'unset_as_default'}}{{else}}{{t 'set_as_default'}}{{/if}}
- </button>
- <button id="quality-gate-delete" class="button-red">{{t 'delete'}}</button>
- </div>
- </div>
-{{/if}}
diff --git a/server/sonar-web/src/main/js/apps/quality-gates/templates/quality-gate-detail.hbs b/server/sonar-web/src/main/js/apps/quality-gates/templates/quality-gate-detail.hbs
deleted file mode 100644
index 0c5467b3476..00000000000
--- a/server/sonar-web/src/main/js/apps/quality-gates/templates/quality-gate-detail.hbs
+++ /dev/null
@@ -1,2 +0,0 @@
-<div id="quality-gate-conditions" class="quality-gate-section"></div>
-<div id="quality-gate-projects" class="quality-gate-section"></div> \ No newline at end of file
diff --git a/server/sonar-web/src/main/js/apps/quality-gates/templates/quality-gates-gate.hbs b/server/sonar-web/src/main/js/apps/quality-gates/templates/quality-gates-gate.hbs
deleted file mode 100644
index d39ea6809de..00000000000
--- a/server/sonar-web/src/main/js/apps/quality-gates/templates/quality-gates-gate.hbs
+++ /dev/null
@@ -1,13 +0,0 @@
-<table>
- <tr>
- <td class="text-top">{{name}}</td>
- <td class="text-top thin nowrap spacer-left">
- {{#if isDefault}}
- <span class="badge pull-right">{{t 'default'}}</span>
- {{/if}}
- </td>
- </tr>
-</table>
-
-
-
diff --git a/server/sonar-web/src/main/js/apps/quality-gates/templates/quality-gates-gates.hbs b/server/sonar-web/src/main/js/apps/quality-gates/templates/quality-gates-gates.hbs
deleted file mode 100644
index 8022059ffad..00000000000
--- a/server/sonar-web/src/main/js/apps/quality-gates/templates/quality-gates-gates.hbs
+++ /dev/null
@@ -1 +0,0 @@
-<div class="js-list"></div>
diff --git a/server/sonar-web/src/main/js/apps/quality-gates/templates/quality-gates-intro.hbs b/server/sonar-web/src/main/js/apps/quality-gates/templates/quality-gates-intro.hbs
deleted file mode 100644
index a16e566c47e..00000000000
--- a/server/sonar-web/src/main/js/apps/quality-gates/templates/quality-gates-intro.hbs
+++ /dev/null
@@ -1,4 +0,0 @@
-<p class="spacer-bottom">Quality Gates are collections of simple boolean thresholds set on project measures. A project
- must pass each of the thresholds in order to pass the Quality Gate as a whole.</p>
-<p>It is possible to set a default Quality Gate, which will be applied to all projects not explicitly assigned to some
- other gate.</p>
diff --git a/server/sonar-web/src/main/js/apps/quality-gates/templates/quality-gates-layout.hbs b/server/sonar-web/src/main/js/apps/quality-gates/templates/quality-gates-layout.hbs
deleted file mode 100644
index 25bd5742e32..00000000000
--- a/server/sonar-web/src/main/js/apps/quality-gates/templates/quality-gates-layout.hbs
+++ /dev/null
@@ -1,11 +0,0 @@
-<div class="search-navigator sticky search-navigator-extended-view">
- <div class="search-navigator-side search-navigator-side-light">
- <div class="search-navigator-filters"></div>
- <div class="quality-gates-results panel"></div>
- </div>
-
- <div class="search-navigator-workspace">
- <div class="search-navigator-workspace-header"></div>
- <div class="search-navigator-workspace-details"></div>
- </div>
-</div>
diff --git a/server/sonar-web/src/main/js/apps/quality-gates/gate-conditions-delete-view.js b/server/sonar-web/src/main/js/apps/quality-gates/views/copy-view.js
index fb8fad5c333..bd20dbdfa8d 100644
--- a/server/sonar-web/src/main/js/apps/quality-gates/gate-conditions-delete-view.js
+++ b/server/sonar-web/src/main/js/apps/quality-gates/views/copy-view.js
@@ -17,10 +17,9 @@
* along with this program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
-import _ from 'underscore';
-import Marionette from 'backbone.marionette';
-import ModalForm from '../../components/common/modal-form';
-import Template from './templates/quality-gates-condition-delete.hbs';
+import ModalForm from '../../../components/common/modal-form';
+import Template from '../templates/quality-gate-form.hbs';
+import { copyQualityGate } from '../../../api/quality-gates';
export default ModalForm.extend({
template: Template,
@@ -32,26 +31,17 @@ export default ModalForm.extend({
},
sendRequest () {
- const that = this;
- const options = {
- statusCode: {
- // do not show global error
- 400: null
- }
- };
- return this.model.destroy(options)
- .done(function () {
- that.destroy();
- }).fail(function (jqXHR) {
- that.enableForm();
- that.showErrors(jqXHR.responseJSON.errors, jqXHR.responseJSON.warnings);
- });
+ const { id } = this.options.qualityGate;
+ const name = this.$('#quality-gate-form-name').val();
+
+ copyQualityGate(id, name).then(qualityGate => {
+ this.destroy();
+ this.options.onCopy(qualityGate);
+ });
},
serializeData () {
- return _.extend(Marionette.ItemView.prototype.serializeData.apply(this, arguments), {
- metric: this.options.metric
- });
+ return { method: 'copy', ...this.options.qualityGate };
}
});
diff --git a/server/sonar-web/src/main/js/apps/quality-gates/create-view.js b/server/sonar-web/src/main/js/apps/quality-gates/views/create-view.js
index 468b6711f81..3af7c15c118 100644
--- a/server/sonar-web/src/main/js/apps/quality-gates/create-view.js
+++ b/server/sonar-web/src/main/js/apps/quality-gates/views/create-view.js
@@ -17,24 +17,30 @@
* along with this program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
-import FormView from './form-view';
+import ModalForm from '../../../components/common/modal-form';
+import Template from '../templates/quality-gate-form.hbs';
+import { createQualityGate } from '../../../api/quality-gates';
-export default FormView.extend({
- method: 'create',
+export default ModalForm.extend({
+ template: Template,
- prepareRequest () {
- const that = this;
- const url = '/api/qualitygates/create';
+ onFormSubmit () {
+ ModalForm.prototype.onFormSubmit.apply(this, arguments);
+ this.disableForm();
+ this.sendRequest();
+ },
+
+ sendRequest () {
const name = this.$('#quality-gate-form-name').val();
- const options = {
- url,
- data: { name }
- };
- return this.sendRequest(options)
- .done(function (r) {
- const gate = that.addGate(r);
- gate.trigger('select', gate);
- });
+
+ createQualityGate(name).then(qualityGate => {
+ this.destroy();
+ this.options.onAdd(qualityGate);
+ });
+ },
+
+ serializeData () {
+ return { method: 'create' };
}
});
diff --git a/server/sonar-web/src/main/js/apps/quality-gates/actions-view.js b/server/sonar-web/src/main/js/apps/quality-gates/views/delete-view.js
index 8e22e0b9b8c..291e866c211 100644
--- a/server/sonar-web/src/main/js/apps/quality-gates/actions-view.js
+++ b/server/sonar-web/src/main/js/apps/quality-gates/views/delete-view.js
@@ -17,29 +17,30 @@
* along with this program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
-import _ from 'underscore';
-import Marionette from 'backbone.marionette';
-import CreateView from './create-view';
-import Template from './templates/quality-gate-actions.hbs';
+import ModalForm from '../../../components/common/modal-form';
+import Template from '../templates/quality-gates-delete.hbs';
+import { deleteQualityGate } from '../../../api/quality-gates';
-export default Marionette.ItemView.extend({
+export default ModalForm.extend({
template: Template,
- events: {
- 'click #quality-gate-add': 'add'
+ onFormSubmit () {
+ ModalForm.prototype.onFormSubmit.apply(this, arguments);
+ this.disableForm();
+ this.sendRequest();
},
- add (e) {
- e.preventDefault();
- new CreateView({
- collection: this.collection
- }).render();
+ sendRequest () {
+ const { id } = this.options.qualityGate;
+
+ deleteQualityGate(id).then(() => {
+ this.destroy();
+ this.options.onDelete(this.options.qualityGate);
+ });
},
serializeData () {
- return _.extend(Marionette.ItemView.prototype.serializeData.apply(this, arguments), {
- canEdit: this.options.canEdit
- });
+ return this.options.qualityGate;
}
});
diff --git a/server/sonar-web/src/main/js/apps/quality-gates/delete-view.js b/server/sonar-web/src/main/js/apps/quality-gates/views/gate-conditions-delete-view.js
index 7efa270d9c1..51aa9aaa27f 100644
--- a/server/sonar-web/src/main/js/apps/quality-gates/delete-view.js
+++ b/server/sonar-web/src/main/js/apps/quality-gates/views/gate-conditions-delete-view.js
@@ -17,8 +17,9 @@
* along with this program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
-import ModalForm from '../../components/common/modal-form';
-import Template from './templates/quality-gates-delete.hbs';
+import ModalForm from '../../../components/common/modal-form';
+import Template from '../templates/quality-gates-condition-delete.hbs';
+import { deleteCondition } from '../../../api/quality-gates';
export default ModalForm.extend({
template: Template,
@@ -30,20 +31,17 @@ export default ModalForm.extend({
},
sendRequest () {
- const that = this;
- const options = {
- statusCode: {
- // do not show global error
- 400: null
- }
- };
- return this.model.destroy(options)
- .done(function () {
- that.destroy();
- }).fail(function (jqXHR) {
- that.enableForm();
- that.showErrors(jqXHR.responseJSON.errors, jqXHR.responseJSON.warnings);
+ return deleteCondition(this.options.condition.id)
+ .then(() => {
+ this.destroy();
+ this.options.onDelete();
});
+ },
+
+ serializeData () {
+ return {
+ metric: this.options.metric
+ };
}
});
diff --git a/server/sonar-web/src/main/js/apps/quality-gates/views/gate-projects-view.js b/server/sonar-web/src/main/js/apps/quality-gates/views/gate-projects-view.js
new file mode 100644
index 00000000000..41d12eee7f7
--- /dev/null
+++ b/server/sonar-web/src/main/js/apps/quality-gates/views/gate-projects-view.js
@@ -0,0 +1,67 @@
+/*
+ * SonarQube
+ * Copyright (C) 2009-2016 SonarSource SA
+ * mailto:contact AT sonarsource DOT com
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+import _ from 'underscore';
+import Marionette from 'backbone.marionette';
+
+import Template from '../templates/quality-gate-detail-projects.hbs';
+import '../../../components/SelectList';
+import { translate } from '../../../helpers/l10n';
+
+export default Marionette.ItemView.extend({
+ template: Template,
+
+ onRender () {
+ const { qualityGate } = this.options;
+
+ new window.SelectList({
+ el: this.options.container,
+ width: '100%',
+ readOnly: !this.options.edit,
+ focusSearch: false,
+ format (item) {
+ return item.name;
+ },
+ searchUrl: '/api/qualitygates/search?gateId=' + qualityGate.id,
+ selectUrl: '/api/qualitygates/select',
+ deselectUrl: '/api/qualitygates/deselect',
+ extra: {
+ gateId: qualityGate.id
+ },
+ selectParameter: 'projectId',
+ selectParameterValue: 'id',
+ labels: {
+ selected: translate('quality_gates.projects.with'),
+ deselected: translate('quality_gates.projects.without'),
+ all: translate('quality_gates.projects.all'),
+ noResults: translate('quality_gates.projects.noResults')
+ },
+ tooltips: {
+ select: translate('quality_gates.projects.select_hint'),
+ deselect: translate('quality_gates.projects.deselect_hint')
+ }
+ });
+ },
+
+ serializeData () {
+ return _.extend(Marionette.ItemView.prototype.serializeData.apply(this, arguments), {
+ canEdit: this.options.edit
+ });
+ }
+});
diff --git a/server/sonar-web/src/main/js/apps/quality-gates/rename-view.js b/server/sonar-web/src/main/js/apps/quality-gates/views/rename-view.js
index 9c5d5b901b9..b3e0551bd5b 100644
--- a/server/sonar-web/src/main/js/apps/quality-gates/rename-view.js
+++ b/server/sonar-web/src/main/js/apps/quality-gates/views/rename-view.js
@@ -17,23 +17,31 @@
* along with this program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
-import FormView from './form-view';
+import ModalForm from '../../../components/common/modal-form';
+import Template from '../templates/quality-gate-form.hbs';
+import { renameQualityGate } from '../../../api/quality-gates';
-export default FormView.extend({
- method: 'rename',
+export default ModalForm.extend({
+ template: Template,
- prepareRequest () {
- const that = this;
- const url = '/api/qualitygates/rename';
+ onFormSubmit () {
+ ModalForm.prototype.onFormSubmit.apply(this, arguments);
+ this.disableForm();
+ this.sendRequest();
+ },
+
+ sendRequest () {
+ const { id } = this.options.qualityGate;
const name = this.$('#quality-gate-form-name').val();
- const options = {
- url,
- data: { id: this.model.id, name }
- };
- return this.sendRequest(options)
- .done(function (r) {
- that.model.set(r);
- });
+
+ renameQualityGate(id, name).then(() => {
+ this.destroy();
+ this.options.onRename(this.options.qualityGate, name);
+ });
+ },
+
+ serializeData () {
+ return { method: 'rename', ...this.options.qualityGate };
}
});
diff --git a/server/sonar-web/src/main/js/apps/quality-profiles/templates/quality-profiles-intro.hbs b/server/sonar-web/src/main/js/apps/quality-profiles/templates/quality-profiles-intro.hbs
index ae369f7f707..469fd1078a4 100644
--- a/server/sonar-web/src/main/js/apps/quality-profiles/templates/quality-profiles-intro.hbs
+++ b/server/sonar-web/src/main/js/apps/quality-profiles/templates/quality-profiles-intro.hbs
@@ -1,3 +1,5 @@
-<p class="spacer-bottom">Quality Profiles are collections of rules to apply during an analysis.</p>
-<p>For each language there is a default profile. All projects not explicitly assigned to some other profile will be
- analyzed with the default.</p>
+<div class="search-navigator-intro markdown">
+ <p>Quality Profiles are collections of rules to apply during an analysis.</p>
+ <p>For each language there is a default profile. All projects not explicitly assigned to some other profile will be
+ analyzed with the default.</p>
+</div>
diff --git a/server/sonar-web/src/main/js/components/navigator/filters/ajax-select-filters.js b/server/sonar-web/src/main/js/components/navigator/filters/ajax-select-filters.js
index 2d77c5d8849..a3402330ef8 100644
--- a/server/sonar-web/src/main/js/components/navigator/filters/ajax-select-filters.js
+++ b/server/sonar-web/src/main/js/components/navigator/filters/ajax-select-filters.js
@@ -234,7 +234,7 @@ const AjaxSelectFilterView = ChoiceFilters.ChoiceFilterView.extend({
initialize (options) {
ChoiceFilters.ChoiceFilterView.prototype.initialize.call(this, {
- detailsView: (options && options.detailsView) ? options.detailsView : AjaxSelectDetailsFilterView
+ projectsView: (options && options.projectsView) ? options.projectsView : AjaxSelectDetailsFilterView
});
},
@@ -343,7 +343,7 @@ const AjaxSelectFilterView = ChoiceFilters.ChoiceFilterView.extend({
onRestore () {
- this.detailsView.updateLists();
+ this.projectsView.updateLists();
this.renderBase();
},
@@ -367,7 +367,7 @@ const ComponentFilterView = AjaxSelectFilterView.extend({
initialize () {
AjaxSelectFilterView.prototype.initialize.call(this, {
- detailsView: AjaxSelectDetailsFilterView
+ projectsView: AjaxSelectDetailsFilterView
});
this.choices = new ComponentSuggestions();
},
@@ -396,7 +396,7 @@ const ProjectFilterView = AjaxSelectFilterView.extend({
initialize () {
BaseFilters.BaseFilterView.prototype.initialize.call(this, {
- detailsView: AjaxSelectDetailsFilterView
+ projectsView: AjaxSelectDetailsFilterView
});
this.choices = new ProjectSuggestions();
@@ -427,7 +427,7 @@ const AssigneeFilterView = AjaxSelectFilterView.extend({
initialize () {
BaseFilters.BaseFilterView.prototype.initialize.call(this, {
- detailsView: AjaxSelectDetailsFilterView
+ projectsView: AjaxSelectDetailsFilterView
});
this.choices = new UserSuggestions();
@@ -457,7 +457,7 @@ const ReporterFilterView = AjaxSelectFilterView.extend({
initialize () {
BaseFilters.BaseFilterView.prototype.initialize.call(this, {
- detailsView: AjaxSelectDetailsFilterView
+ projectsView: AjaxSelectDetailsFilterView
});
this.selection = new UserSuggestions();
diff --git a/server/sonar-web/src/main/js/components/navigator/filters/base-filters.js b/server/sonar-web/src/main/js/components/navigator/filters/base-filters.js
index 3918ef0dc09..44705c1de1b 100644
--- a/server/sonar-web/src/main/js/components/navigator/filters/base-filters.js
+++ b/server/sonar-web/src/main/js/components/navigator/filters/base-filters.js
@@ -87,8 +87,8 @@ const BaseFilterView = Marionette.ItemView.extend({
initialize (options) {
Marionette.ItemView.prototype.initialize.apply(this, arguments);
- const detailsView = (options && options.detailsView) || DetailsFilterView;
- this.detailsView = new detailsView({
+ const detailsView = (options && options.projectsView) || DetailsFilterView;
+ this.projectsView = new detailsView({
model: this.model,
filterView: this
});
@@ -98,7 +98,7 @@ const BaseFilterView = Marionette.ItemView.extend({
attachDetailsView () {
- this.detailsView.$el.detach().appendTo($('body'));
+ this.projectsView.$el.detach().appendTo($('body'));
},
@@ -106,7 +106,7 @@ const BaseFilterView = Marionette.ItemView.extend({
this.renderBase();
this.attachDetailsView();
- this.detailsView.render();
+ this.projectsView.render();
this.$el.toggleClass(
'navigator-filter-disabled',
@@ -156,9 +156,9 @@ const BaseFilterView = Marionette.ItemView.extend({
const top = this.$el.offset().top + this.$el.outerHeight() - 1;
const left = this.$el.offset().left;
- this.detailsView.$el.css({ top, left }).addClass('active');
+ this.projectsView.$el.css({ top, left }).addClass('active');
this.$el.addClass('active');
- this.detailsView.onShow();
+ this.projectsView.onShow();
},
@@ -169,9 +169,9 @@ const BaseFilterView = Marionette.ItemView.extend({
hideDetails () {
- this.detailsView.$el.removeClass('active');
+ this.projectsView.$el.removeClass('active');
this.$el.removeClass('active');
- this.detailsView.onHide();
+ this.projectsView.onHide();
},
diff --git a/server/sonar-web/src/main/js/components/navigator/filters/choice-filters.js b/server/sonar-web/src/main/js/components/navigator/filters/choice-filters.js
index 6a43959e55e..1bf7b2f4cc7 100644
--- a/server/sonar-web/src/main/js/components/navigator/filters/choice-filters.js
+++ b/server/sonar-web/src/main/js/components/navigator/filters/choice-filters.js
@@ -216,7 +216,7 @@ const ChoiceFilterView = BaseFilters.BaseFilterView.extend({
initialize (options) {
BaseFilters.BaseFilterView.prototype.initialize.call(this, {
- detailsView: (options && options.detailsView) ? options.detailsView : DetailsChoiceFilterView
+ projectsView: (options && options.projectsView) ? options.projectsView : DetailsChoiceFilterView
});
let index = 0;
@@ -367,9 +367,9 @@ const ChoiceFilterView = BaseFilters.BaseFilterView.extend({
});
}
this.model.unset('value');
- this.detailsView.render();
- if (this.detailsView.updateCurrent) {
- this.detailsView.updateCurrent(0);
+ this.projectsView.render();
+ if (this.projectsView.updateCurrent) {
+ this.projectsView.updateCurrent(0);
}
},
diff --git a/server/sonar-web/src/main/js/components/navigator/filters/favorite-filters.js b/server/sonar-web/src/main/js/components/navigator/filters/favorite-filters.js
index 3852d32983e..11b769a903a 100644
--- a/server/sonar-web/src/main/js/components/navigator/filters/favorite-filters.js
+++ b/server/sonar-web/src/main/js/components/navigator/filters/favorite-filters.js
@@ -69,7 +69,7 @@ const FavoriteFilterView = ChoiceFilters.ChoiceFilterView.extend({
initialize () {
ChoiceFilters.ChoiceFilterView.prototype.initialize.call(this, {
- detailsView: DetailsFavoriteFilterView
+ projectsView: DetailsFavoriteFilterView
});
},
diff --git a/server/sonar-web/src/main/js/components/navigator/filters/metric-filters.js b/server/sonar-web/src/main/js/components/navigator/filters/metric-filters.js
index f460810eee6..5a3be4e8f12 100644
--- a/server/sonar-web/src/main/js/components/navigator/filters/metric-filters.js
+++ b/server/sonar-web/src/main/js/components/navigator/filters/metric-filters.js
@@ -125,7 +125,7 @@ export default BaseFilters.BaseFilterView.extend({
initialize () {
BaseFilters.BaseFilterView.prototype.initialize.call(this, {
- detailsView: DetailsMetricFilterView
+ projectsView: DetailsMetricFilterView
});
this.groupMetrics();
diff --git a/server/sonar-web/src/main/js/components/navigator/filters/more-criteria-filters.js b/server/sonar-web/src/main/js/components/navigator/filters/more-criteria-filters.js
index 327f9f4d89c..0d148321b1a 100644
--- a/server/sonar-web/src/main/js/components/navigator/filters/more-criteria-filters.js
+++ b/server/sonar-web/src/main/js/components/navigator/filters/more-criteria-filters.js
@@ -82,7 +82,7 @@ const MoreCriteriaFilterView = ChoiceFilters.ChoiceFilterView.extend({
initialize () {
ChoiceFilters.ChoiceFilterView.prototype.initialize.call(this, {
- detailsView: DetailsMoreCriteriaFilterView
+ projectsView: DetailsMoreCriteriaFilterView
});
},
diff --git a/server/sonar-web/src/main/js/components/navigator/filters/range-filters.js b/server/sonar-web/src/main/js/components/navigator/filters/range-filters.js
index 3fc177c1a0a..964e73d10a7 100644
--- a/server/sonar-web/src/main/js/components/navigator/filters/range-filters.js
+++ b/server/sonar-web/src/main/js/components/navigator/filters/range-filters.js
@@ -72,7 +72,7 @@ const RangeFilterView = BaseFilters.BaseFilterView.extend({
initialize () {
BaseFilters.BaseFilterView.prototype.initialize.call(this, {
- detailsView: DetailsRangeFilterView
+ projectsView: DetailsRangeFilterView
});
},
@@ -140,7 +140,7 @@ const RangeFilterView = BaseFilters.BaseFilterView.extend({
enabled: true
});
- this.detailsView.populateInputs();
+ this.projectsView.populateInputs();
}
},
@@ -160,7 +160,7 @@ const RangeFilterView = BaseFilters.BaseFilterView.extend({
}
});
- this.detailsView.updateLists();
+ this.projectsView.updateLists();
this.model.set({
value,
@@ -177,7 +177,7 @@ const RangeFilterView = BaseFilters.BaseFilterView.extend({
clear () {
this.model.unset('value');
- this.detailsView.render();
+ this.projectsView.render();
}
});
@@ -187,7 +187,7 @@ const DateRangeFilterView = RangeFilterView.extend({
render () {
RangeFilterView.prototype.render.apply(this, arguments);
- this.detailsView.$('input')
+ this.projectsView.$('input')
.prop('placeholder', '1970-01-31')
.datepicker({
dateFormat: 'yy-mm-dd',
diff --git a/server/sonar-web/src/main/js/components/navigator/filters/string-filters.js b/server/sonar-web/src/main/js/components/navigator/filters/string-filters.js
index 5a1d11b11da..b6f6f8124db 100644
--- a/server/sonar-web/src/main/js/components/navigator/filters/string-filters.js
+++ b/server/sonar-web/src/main/js/components/navigator/filters/string-filters.js
@@ -55,7 +55,7 @@ export default BaseFilters.BaseFilterView.extend({
initialize () {
BaseFilters.BaseFilterView.prototype.initialize.call(this, {
- detailsView: DetailsStringFilterView
+ projectsView: DetailsStringFilterView
});
},
@@ -90,7 +90,7 @@ export default BaseFilters.BaseFilterView.extend({
clear () {
this.model.unset('value');
- this.detailsView.render();
+ this.projectsView.render();
}
});
diff --git a/server/sonar-web/src/main/js/components/store/configureStore.js b/server/sonar-web/src/main/js/components/store/configureStore.js
index fee2f603bf2..d7d69d3ecea 100644
--- a/server/sonar-web/src/main/js/components/store/configureStore.js
+++ b/server/sonar-web/src/main/js/components/store/configureStore.js
@@ -17,18 +17,24 @@
* along with this program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
-import { createStore, applyMiddleware } from 'redux';
+import { createStore, applyMiddleware, compose } from 'redux';
import thunk from 'redux-thunk';
const middlewares = [thunk];
+const composed = [];
if (process.env.NODE_ENV !== 'production') {
const createLogger = require('redux-logger');
middlewares.push(createLogger());
+
+ composed.push(window.devToolsExtension ? window.devToolsExtension() : f => f);
}
-const createStoreWithMiddleware = applyMiddleware(...middlewares)(createStore);
+const finalCreateStore = compose(
+ applyMiddleware(...middlewares),
+ ...composed
+)(createStore);
export default function configureStore (rootReducer) {
- return createStoreWithMiddleware(rootReducer);
+ return finalCreateStore(rootReducer);
}
diff --git a/server/sonar-web/src/main/less/components/react-select.less b/server/sonar-web/src/main/less/components/react-select.less
index 2ef178979c5..05475d007e8 100644
--- a/server/sonar-web/src/main/less/components/react-select.less
+++ b/server/sonar-web/src/main/less/components/react-select.less
@@ -24,6 +24,7 @@
display: inline-block;
vertical-align: middle;
font-size: @smallFontSize;
+ text-align: left;
}
.Select,
@@ -52,6 +53,7 @@
width: 100%;
height: @formControlHeight;
border: 1px solid @darkGrey;
+ border-collapse: separate;
border-radius: 2px;
background-color: #fff;
color: @baseFontColor;
@@ -258,7 +260,7 @@
position: absolute;
top: 100%;
width: 100%;
- z-index: 1;
+ z-index: @dropdown-menu-z-index;
-webkit-overflow-scrolling: touch;
box-shadow: @defaultShadow;
}
diff --git a/server/sonar-web/src/main/less/components/search-navigator.less b/server/sonar-web/src/main/less/components/search-navigator.less
index 9849cb77707..33a84b5f6a2 100644
--- a/server/sonar-web/src/main/less/components/search-navigator.less
+++ b/server/sonar-web/src/main/less/components/search-navigator.less
@@ -532,3 +532,9 @@
display: block;
}
}
+
+.search-navigator-intro {
+ width: 500px;
+ margin: 0 auto;
+ padding-top: 100px;
+}
diff --git a/server/sonar-web/src/main/less/pages/quality-gates.less b/server/sonar-web/src/main/less/pages/quality-gates.less
index 05cb151e4c9..a4dfdd95d61 100644
--- a/server/sonar-web/src/main/less/pages/quality-gates.less
+++ b/server/sonar-web/src/main/less/pages/quality-gates.less
@@ -92,6 +92,9 @@
}
}
+.quality-gate-section {
+ max-width: 1440px;
+}
.quality-gate-section + .quality-gate-section {
margin-top: @navigatorPadding;
diff --git a/sonar-core/src/main/resources/org/sonar/l10n/core.properties b/sonar-core/src/main/resources/org/sonar/l10n/core.properties
index 13ed09f521f..fcf3c58a316 100644
--- a/sonar-core/src/main/resources/org/sonar/l10n/core.properties
+++ b/sonar-core/src/main/resources/org/sonar/l10n/core.properties
@@ -1861,7 +1861,16 @@ quality_gates.delete_condition.confirm.message=Are you sure you want to delete t
quality_gates.project_period=over period {0} - defined at project level
quality_gates.warning_tooltip=Warning Threshold
quality_gates.error_tooltip=Error Threshold
-
+quality_gates.condition.leak.yes=Yes
+quality_gates.condition.leak.no=No
+quality_gates.condition.leak.unconditional=Always
+quality_gates.conditions.metric=Metric
+quality_gates.conditions.leak=Over Leak Period
+quality_gates.conditions.operator=Operator
+quality_gates.conditions.warning=Warning
+quality_gates.conditions.error=Error
+quality_gates.intro.1=Quality Gates are collections of simple boolean thresholds set on project measures. A project must pass each of the thresholds in order to pass the Quality Gate as a whole.
+quality_gates.intro.2=It is possible to set a default Quality Gate, which will be applied to all projects not explicitly assigned to some other gate.
#------------------------------------------------------------------------------
#