]> source.dussan.org Git - sonarqube.git/commitdiff
extend the main js app (#1345)
authorStas Vilchik <vilchiks@gmail.com>
Fri, 4 Nov 2016 08:59:58 +0000 (09:59 +0100)
committerGitHub <noreply@github.com>
Fri, 4 Nov 2016 08:59:58 +0000 (09:59 +0100)
28 files changed:
server/sonar-web/config/webpack/webpack.config.base.js
server/sonar-web/src/main/js/app/index.js
server/sonar-web/src/main/js/app/store/rootReducer.js
server/sonar-web/src/main/js/apps/quality-gates/app.js [deleted file]
server/sonar-web/src/main/js/apps/quality-gates/components/Details.js
server/sonar-web/src/main/js/apps/quality-gates/components/List.js
server/sonar-web/src/main/js/apps/quality-gates/components/QualityGatesApp.js
server/sonar-web/src/main/js/apps/quality-gates/containers/DetailsContainer.js
server/sonar-web/src/main/js/apps/quality-gates/containers/QualityGatesAppContainer.js
server/sonar-web/src/main/js/apps/quality-gates/routes.js [new file with mode: 0644]
server/sonar-web/src/main/js/apps/quality-gates/store/actions.js
server/sonar-web/src/main/js/apps/quality-gates/store/reducers.js [deleted file]
server/sonar-web/src/main/js/apps/quality-gates/store/rootReducer.js [new file with mode: 0644]
server/sonar-web/src/main/js/apps/quality-profiles/app.js [deleted file]
server/sonar-web/src/main/js/apps/quality-profiles/changelog/ChangelogContainer.js
server/sonar-web/src/main/js/apps/quality-profiles/compare/ComparisonContainer.js
server/sonar-web/src/main/js/apps/quality-profiles/components/App.js
server/sonar-web/src/main/js/apps/quality-profiles/components/AppContainer.js [new file with mode: 0644]
server/sonar-web/src/main/js/apps/quality-profiles/components/ProfileActions.js
server/sonar-web/src/main/js/apps/quality-profiles/components/ProfileLink.js
server/sonar-web/src/main/js/apps/quality-profiles/components/ProfileNotFound.js
server/sonar-web/src/main/js/apps/quality-profiles/details/ProfileHeader.js
server/sonar-web/src/main/js/apps/quality-profiles/home/PageHeader.js
server/sonar-web/src/main/js/apps/quality-profiles/home/ProfilesList.js
server/sonar-web/src/main/js/apps/quality-profiles/home/ProfilesListHeader.js
server/sonar-web/src/main/js/apps/quality-profiles/routes.js [new file with mode: 0644]
server/sonar-web/src/main/webapp/WEB-INF/app/views/profiles/index.html.erb
server/sonar-web/src/main/webapp/WEB-INF/app/views/quality_gates/index.html.erb

index 336f414ec60997cea7ffda1526488315b8da6694..bb4c81e63fc648396b6b7e93203e34092346fabc 100644 (file)
@@ -46,8 +46,6 @@ module.exports = {
     'project-admin': './src/main/js/apps/project-admin/app.js',
     'project-permissions': './src/main/js/apps/permissions/project/app.js',
     'projects-admin': './src/main/js/apps/projects-admin/app.js',
-    'quality-gates': './src/main/js/apps/quality-gates/app.js',
-    'quality-profiles': './src/main/js/apps/quality-profiles/app.js',
     'settings': './src/main/js/apps/settings/app.js',
     'source-viewer': './src/main/js/apps/source-viewer/app.js',
     'system': './src/main/js/apps/system/app.js',
index addf577ee0787f70484f16e58b6360d41b4ec51d..107dc2ae53c7c670417cfe7d70bb71ff8b5b9af5 100644 (file)
@@ -24,7 +24,9 @@ import { createHistory } from 'history';
 import { Provider } from 'react-redux';
 import App from './components/App';
 import accountRoutes from '../apps/account/routes';
-import projectsRouters from '../apps/projects/routes';
+import projectsRoutes from '../apps/projects/routes';
+import qualityGatesRoutes from '../apps/quality-gates/routes';
+import qualityProfilesRoutes from '../apps/quality-profiles/routes';
 import configureStore from '../components/store/configureStore';
 import rootReducer from './store/rootReducer';
 import './styles/index';
@@ -43,7 +45,9 @@ window.sonarqube.appStarted.then(options => {
         <Router history={history}>
           <Route path="/" component={App}>
             {accountRoutes}
-            {projectsRouters}
+            {projectsRoutes}
+            {qualityGatesRoutes}
+            {qualityProfilesRoutes}
           </Route>
         </Router>
       </Provider>
index a46042491fa238d3eb9b2661bbc0850faf4c6d2f..a317c982af6cd049a322b4a436f8118ed88f58cb 100644 (file)
@@ -27,6 +27,7 @@ import globalMessages, * as fromGlobalMessages from '../../components/store/glob
 
 import issuesActivity, * as fromIssuesActivity from '../../apps/account/home/store/reducer';
 import projectsApp, * as fromProjectsApp from '../../apps/projects/store/reducer';
+import qualityGatesApp from '../../apps/quality-gates/store/rootReducer';
 
 export default combineReducers({
   components,
@@ -38,7 +39,8 @@ export default combineReducers({
 
   // apps
   issuesActivity,
-  projectsApp
+  projectsApp,
+  qualityGatesApp
 });
 
 export const getComponent = (state, key) => (
@@ -88,3 +90,7 @@ export const getProjectsAppFacetByProperty = (state, property) => (
 export const getProjectsAppMaxFacetValue = state => (
     fromProjectsApp.getMaxFacetValue(state.projectsApp)
 );
+
+export const getQualityGatesAppState = state => (
+    state.qualityGatesApp
+);
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
deleted file mode 100644 (file)
index ded4a82..0000000
+++ /dev/null
@@ -1,61 +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 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 } 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: window.baseUrl + '/quality_gates'
-  });
-
-  const finalReducer = combineReducers({
-    rootReducer,
-    routing: routerReducer
-  });
-
-  const store = configureStore(finalReducer);
-
-  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);
-});
index 0b0b2766c23b4485ab19b97848b9588b36912133..d02601bfe6ec1231876e640a281e74cf0c402009 100644 (file)
@@ -65,7 +65,7 @@ export default class Details extends Component {
       qualityGate,
       onCopy: (newQualityGate) => {
         onCopy(newQualityGate);
-        router.push(`/show/${newQualityGate.id}`);
+        router.push(`/quality_gates/show/${newQualityGate.id}`);
       }
     }).render();
   }
@@ -90,7 +90,7 @@ export default class Details extends Component {
       qualityGate,
       onDelete: (qualityGate) => {
         onDelete(qualityGate);
-        router.replace('/');
+        router.replace('/quality_gates');
       }
     }).render();
   }
index 88a9a16f1075da31fd6032bae0a536c0b49ee2c8..66b8ec046ef9dc638c5478805057ea2e77f994f4 100644 (file)
@@ -28,7 +28,7 @@ export default function List ({ qualityGates }) {
         {qualityGates.map(qualityGate => (
             <Link
                 key={qualityGate.id}
-                to={`/show/${qualityGate.id}`}
+                to={`/quality_gates/show/${qualityGate.id}`}
                 activeClassName="active"
                 className="list-group-item"
                 data-id={qualityGate.id}>
index 8d3d4838a6c15e6c5230725d12ad6a136f25604b..9395bac2b258d93dbb869db79c0b71e6c24bd9e9 100644 (file)
@@ -51,7 +51,7 @@ export default class QualityGatesApp extends Component {
     const { router } = this.context;
 
     addQualityGate(qualityGate);
-    router.push(`/show/${qualityGate.id}`);
+    router.push(`/quality_gates/show/${qualityGate.id}`);
   }
 
   render () {
index 3f2c835edf51a6608cb5a13fc00e8b7b0dadd837..ae53d592b691207ad366baa93f44a1937a34c5df 100644 (file)
  * 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
+  deleteQualityGate,
+  showQualityGate,
+  renameQualityGate,
+  copyQualityGate,
+  setQualityGateAsDefault,
+  unsetQualityGateAsDefault,
+  addCondition,
+  deleteCondition,
+  saveCondition
 } from '../store/actions';
 import Details from '../components/Details';
+import { getQualityGatesAppState } from '../../../app/store/rootReducer';
 
-function mapStateToProps (state) {
-  return state.rootReducer;
-}
+const mapStateToProps = state => (
+    getQualityGatesAppState(state)
+);
 
-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))
-  };
-}
+const mapDispatchToProps = dispatch => ({
+  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,
index 2145fd817ea93e456c5cbadc5c606160c047649a..4eaf763bb2b80420a74096ca905108de5773cfed 100644 (file)
@@ -21,18 +21,17 @@ import { connect } from 'react-redux';
 
 import { setState, addQualityGate, deleteQualityGate } from '../store/actions';
 import QualityGateApp from '../components/QualityGatesApp';
+import { getQualityGatesAppState } from '../../../app/store/rootReducer';
 
-function mapStateToProps (state) {
-  return state.rootReducer;
-}
+const mapStateToProps = state => (
+    getQualityGatesAppState(state)
+);
 
-function mapDispatchToProps (dispatch) {
-  return {
-    updateStore: nextState => dispatch(setState(nextState)),
-    addQualityGate: qualityGate => dispatch(addQualityGate(qualityGate)),
-    deleteQualityGate: qualityGate => dispatch(deleteQualityGate(qualityGate))
-  };
-}
+const mapDispatchToProps = dispatch => ({
+  updateStore: nextState => dispatch(setState(nextState)),
+  addQualityGate: qualityGate => dispatch(addQualityGate(qualityGate)),
+  deleteQualityGate: qualityGate => dispatch(deleteQualityGate(qualityGate))
+});
 
 export default connect(
     mapStateToProps,
diff --git a/server/sonar-web/src/main/js/apps/quality-gates/routes.js b/server/sonar-web/src/main/js/apps/quality-gates/routes.js
new file mode 100644 (file)
index 0000000..8f471c3
--- /dev/null
@@ -0,0 +1,33 @@
+/*
+ * 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 { Route, IndexRoute, Redirect } from 'react-router';
+import QualityGatesAppContainer from './containers/QualityGatesAppContainer';
+import Intro from './components/Intro';
+import DetailsContainer from './containers/DetailsContainer';
+
+export default (
+    <Route path="quality_gates" component={QualityGatesAppContainer}>
+      <Redirect from="/quality_gates/index" to="/quality_gates/"/>
+
+      <IndexRoute component={Intro}/>
+      <Route path="show/:id" component={DetailsContainer}/>
+    </Route>
+);
index 201fb4c4e6b03de9e3cc27bf8e1a35583056c16e..280d81aebdf0c02270ae3e817b2c8a3a1f1ae6f8 100644 (file)
@@ -17,7 +17,7 @@
  * 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 const SET_STATE = 'qualityGates/SET_STATE';
 export function setState (nextState) {
   return {
     type: SET_STATE,
@@ -25,40 +25,40 @@ export function setState (nextState) {
   };
 }
 
-export const ADD = 'ADD';
+export const ADD = 'qualityGates/ADD';
 export function addQualityGate (qualityGate) {
   return {
-    type: 'ADD',
+    type: ADD,
     qualityGate
   };
 }
 
-export const DELETE = 'DELETE';
+export const DELETE = 'qualityGates/DELETE';
 export function deleteQualityGate (qualityGate) {
   return {
-    type: 'DELETE',
+    type: DELETE,
     qualityGate
   };
 }
 
-export const SHOW = 'SHOW';
+export const SHOW = 'qualityGates/SHOW';
 export function showQualityGate (qualityGate) {
   return {
-    type: 'SHOW',
+    type: SHOW,
     qualityGate
   };
 }
 
-export const RENAME = 'RENAME';
+export const RENAME = 'qualityGates/RENAME';
 export function renameQualityGate (qualityGate, newName) {
   return {
-    type: 'RENAME',
+    type: RENAME,
     qualityGate,
     newName
   };
 }
 
-export const COPY = 'COPY';
+export const COPY = 'qualityGates/COPY';
 export function copyQualityGate (qualityGate) {
   return {
     type: COPY,
@@ -74,7 +74,7 @@ export function setQualityGateAsDefault (qualityGate) {
   };
 }
 
-export const UNSET_AS_DEFAULT = 'UNSET_AS_DEFAULT';
+export const UNSET_AS_DEFAULT = 'qualityGates/UNSET_AS_DEFAULT';
 export function unsetQualityGateAsDefault (qualityGate) {
   return {
     type: UNSET_AS_DEFAULT,
@@ -82,7 +82,7 @@ export function unsetQualityGateAsDefault (qualityGate) {
   };
 }
 
-export const ADD_CONDITION = 'ADD_CONDITION';
+export const ADD_CONDITION = 'qualityGates/ADD_CONDITION';
 export function addCondition (metric) {
   return {
     type: ADD_CONDITION,
@@ -90,7 +90,7 @@ export function addCondition (metric) {
   };
 }
 
-export const SAVE_CONDITION = 'SAVE_CONDITION';
+export const SAVE_CONDITION = 'qualityGates/SAVE_CONDITION';
 export function saveCondition (oldCondition, newCondition) {
   return {
     type: SAVE_CONDITION,
@@ -99,7 +99,7 @@ export function saveCondition (oldCondition, newCondition) {
   };
 }
 
-export const DELETE_CONDITION = 'DELETE_CONDITION';
+export const DELETE_CONDITION = 'qualityGates/DELETE_CONDITION';
 export function deleteCondition (condition) {
   return {
     type: DELETE_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
deleted file mode 100644 (file)
index 0b8e06d..0000000
+++ /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 {
-    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/rootReducer.js b/server/sonar-web/src/main/js/apps/quality-gates/store/rootReducer.js
new file mode 100644 (file)
index 0000000..0b8e06d
--- /dev/null
@@ -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-profiles/app.js b/server/sonar-web/src/main/js/apps/quality-profiles/app.js
deleted file mode 100644 (file)
index cb998c8..0000000
+++ /dev/null
@@ -1,59 +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 React from 'react';
-import { render } from 'react-dom';
-import {
-    Router,
-    Route,
-    IndexRoute,
-    Redirect,
-    useRouterHistory
-} from 'react-router';
-import { createHistory } from 'history';
-import App from './components/App';
-import ProfileContainer from './components/ProfileContainer';
-import HomeContainer from './home/HomeContainer';
-import ProfileDetails from './details/ProfileDetails';
-import ChangelogContainer from './changelog/ChangelogContainer';
-import ComparisonContainer from './compare/ComparisonContainer';
-
-window.sonarqube.appStarted.then(options => {
-  const el = document.querySelector(options.el);
-
-  const history = useRouterHistory(createHistory)({
-    basename: window.baseUrl + '/profiles'
-  });
-
-  render((
-      <Router history={history}>
-        <Route path="/" component={App}>
-          <Redirect from="/index" to="/"/>
-
-          <IndexRoute component={HomeContainer}/>
-
-          <Route component={ProfileContainer}>
-            <Route path="show" component={ProfileDetails}/>
-            <Route path="changelog" component={ChangelogContainer}/>
-            <Route path="compare" component={ComparisonContainer}/>
-          </Route>
-        </Route>
-      </Router>
-  ), el);
-});
index 2c0bdd1c8826766fdb562fe5525537730991cbaf..59adf9809dd241a973f7e034bdfda7aafadbc74b 100644 (file)
@@ -114,17 +114,17 @@ export default class ChangelogContainer extends React.Component {
 
   handleFromDateChange (fromDate) {
     const query = { ...this.props.location.query, since: fromDate };
-    this.context.router.push({ pathname: '/changelog', query });
+    this.context.router.push({ pathname: '/profiles/changelog', query });
   }
 
   handleToDateChange (toDate) {
     const query = { ...this.props.location.query, to: toDate };
-    this.context.router.push({ pathname: '/changelog', query });
+    this.context.router.push({ pathname: '/profiles/changelog', query });
   }
 
   handleReset () {
     const query = { key: this.props.profile.key };
-    this.context.router.push({ pathname: '/changelog', query });
+    this.context.router.push({ pathname: '/profiles/changelog', query });
   }
 
   render () {
index 03d5d1452125cdcecd47dcb02835d1063ba467a4..24bc4b3366814115d1707efb10035e622c4db2da 100644 (file)
@@ -81,7 +81,7 @@ export default class ComparisonContainer extends React.Component {
 
   handleCompare (withKey) {
     this.context.router.push({
-      pathname: '/compare',
+      pathname: '/profiles/compare',
       query: {
         key: this.props.profile.key,
         withKey
index 2c2eff071c9587103fa827d32dc3d56a724db0aa..75c7354fd060173210e91022d244ca7a49984330 100644 (file)
  * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
  */
 import React from 'react';
-import { getLanguages } from '../../../api/languages';
-import {
-    getQualityProfiles,
-    getExporters
-} from '../../../api/quality-profiles';
+import { getQualityProfiles, getExporters } from '../../../api/quality-profiles';
 import { getCurrentUser } from '../../../api/users';
 import '../styles.css';
 import { sortProfiles } from '../utils';
@@ -49,15 +45,13 @@ export default class App extends React.Component {
     this.setState({ loading: true });
     Promise.all([
       getCurrentUser(),
-      getLanguages(),
       getExporters(),
       getQualityProfiles()
     ]).then(responses => {
       if (this.mounted) {
-        const [user, languages, exporters, profiles] = responses;
+        const [user, exporters, profiles] = responses;
         const canAdmin = user.permissions.global.includes('profileadmin');
         this.setState({
-          languages,
           exporters,
           canAdmin,
           profiles: sortProfiles(profiles),
@@ -76,13 +70,16 @@ export default class App extends React.Component {
   }
 
   renderChild () {
-    if (this.state.loading) {
+    const areLanguagesLoading = Object.keys(this.props.languages).length === 0;
+    if (this.state.loading || areLanguagesLoading) {
       return <i className="spinner"/>;
     }
 
+    const finalLanguages = Object.values(this.props.languages);
+
     return React.cloneElement(this.props.children, {
       profiles: this.state.profiles,
-      languages: this.state.languages,
+      languages: finalLanguages,
       exporters: this.state.exporters,
       canAdmin: this.state.canAdmin,
       updateProfiles: this.updateProfiles
diff --git a/server/sonar-web/src/main/js/apps/quality-profiles/components/AppContainer.js b/server/sonar-web/src/main/js/apps/quality-profiles/components/AppContainer.js
new file mode 100644 (file)
index 0000000..3f164b4
--- /dev/null
@@ -0,0 +1,28 @@
+/*
+ * 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 App from './App';
+import { getLanguages } from '../../../app/store/rootReducer';
+
+export default connect(
+    state => ({
+      languages: getLanguages(state)
+    })
+)(App);
index f0a834c05558966b9812f954d1bc256c02c094fc..b79902486e3f13141d376a59f94c465a191a0fd1 100644 (file)
@@ -54,7 +54,7 @@ export default class ProfileActions extends React.Component {
     }).on('done', profile => {
       this.props.updateProfiles().then(() => {
         this.context.router.push({
-          pathname: '/show',
+          pathname: '/profiles/show',
           query: { key: profile.key }
         });
       });
@@ -72,7 +72,7 @@ export default class ProfileActions extends React.Component {
     new DeleteProfileView({
       profile: this.props.profile
     }).on('done', () => {
-      this.context.router.replace('/');
+      this.context.router.replace('/profiles');
       this.props.updateProfiles();
     }).render();
   }
index 1e0d0cfd1dd792fe659608851ae35d5643373cf5..6e15a51668ec683aca2c87076335836d5922c3c5 100644 (file)
@@ -30,7 +30,7 @@ export default class ProfileLink extends React.Component {
     const query = { key: profileKey };
     return (
         <Link
-            to={{ pathname: '/show', query }}
+            to={{ pathname: '/profiles/show', query }}
             activeClassName="link-no-underline"
             {...other}>
           {children}
index 9c37677df4494da1b064b989de59c71edf5be603..c04bb9c10d7205646b990b56558cd2ee82ccfb2a 100644 (file)
@@ -26,7 +26,7 @@ export default class ProfileNotFound extends React.Component {
     return (
         <div className="quality-profile-not-found">
           <div className="note spacer-bottom">
-            <IndexLink to="/" className="text-muted">
+            <IndexLink to="/profiles/" className="text-muted">
               {translate('quality_profiles.page')}
             </IndexLink>
           </div>
index 439be30205c22c105a24bfdb188d59d305862a9a..44182e47a2ddcdba9b7df6ac5ac98fb8bd8489c3 100644 (file)
@@ -86,12 +86,12 @@ export default class ProfileHeader extends React.Component {
     return (
         <header className="page-header quality-profile-header">
           <div className="note spacer-bottom">
-            <IndexLink to="/" className="text-muted">
+            <IndexLink to="/profiles/" className="text-muted">
               {translate('quality_profiles.page')}
             </IndexLink>
             {' / '}
             <Link
-                to={{ pathname: '/', query: { language: profile.language } }}
+                to={{ pathname: '/profiles/', query: { language: profile.language } }}
                 className="text-muted">
               {profile.languageName}
             </Link>
@@ -111,7 +111,7 @@ export default class ProfileHeader extends React.Component {
               {this.renderUsageDate()}
               <li>
                 <Link
-                    to={{ pathname: '/changelog', query: { key: profile.key } }}
+                    to={{ pathname: '/profiles/changelog', query: { key: profile.key } }}
                     className="button">
                   {translate('changelog')}
                 </Link>
index 9d9ed85759fd453c6b51fb760b9308d43c9bb277..1ae3bef24c8672f1a63ee56498ea7e4fe745d8c5 100644 (file)
@@ -64,7 +64,7 @@ export default class PageHeader extends React.Component {
       }).on('done', profile => {
         this.props.updateProfiles().then(() => {
           this.context.router.push({
-            pathname: '/show',
+            pathname: '/profiles/show',
             query: { key: profile.key }
           });
         });
index ba333be7e1281ef5fbb8387f63cea1f814ddbff3..3e3e17651336cc8cef630b3a01aae16ce786a8a0 100644 (file)
@@ -18,7 +18,6 @@
  * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
  */
 import React from 'react';
-import { PropTypes as RouterPropTypes } from 'react-router';
 import groupBy from 'lodash/groupBy';
 import pick from 'lodash/pick';
 import sortBy from 'lodash/sortBy';
@@ -32,7 +31,7 @@ export default class ProfilesList extends React.Component {
   static propTypes = {
     profiles: ProfilesListType,
     languages: LanguagesListType,
-    location: RouterPropTypes.location,
+    location: React.PropTypes.object,
     canAdmin: React.PropTypes.bool.isRequired,
     updateProfiles: React.PropTypes.func.isRequired
   };
index 010321937ce479969d4595fca12ee41fcc1d8d31..8a71385bf4a01e5f56e8693d7197291f3e284cb2 100644 (file)
@@ -52,14 +52,14 @@ export default class ProfilesListHeader extends React.Component {
     return (
         <ul className="dropdown-menu">
           <li>
-            <IndexLink to="/">
+            <IndexLink to="/profiles/">
               {translate('quality_profiles.all_profiles')}
             </IndexLink>
           </li>
           {this.props.languages.map(language => (
               <li key={language.key}>
                 <IndexLink
-                    to={{ pathname: '/', query: { language: language.key } }}
+                    to={{ pathname: '/profiles/', query: { language: language.key } }}
                     className="js-language-filter-option"
                     data-language={language.key}>
                   {language.name}
diff --git a/server/sonar-web/src/main/js/apps/quality-profiles/routes.js b/server/sonar-web/src/main/js/apps/quality-profiles/routes.js
new file mode 100644 (file)
index 0000000..b5729b5
--- /dev/null
@@ -0,0 +1,41 @@
+/*
+ * 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 { Route, IndexRoute, Redirect } from 'react-router';
+import AppContainer from './components/AppContainer';
+import ProfileContainer from './components/ProfileContainer';
+import HomeContainer from './home/HomeContainer';
+import ProfileDetails from './details/ProfileDetails';
+import ChangelogContainer from './changelog/ChangelogContainer';
+import ComparisonContainer from './compare/ComparisonContainer';
+
+export default (
+    <Route path="profiles" component={AppContainer}>
+      <Redirect from="/profiles/index" to="/profiles/"/>
+
+      <IndexRoute component={HomeContainer}/>
+
+      <Route component={ProfileContainer}>
+        <Route path="show" component={ProfileDetails}/>
+        <Route path="changelog" component={ChangelogContainer}/>
+        <Route path="compare" component={ComparisonContainer}/>
+      </Route>
+    </Route>
+);
index b41bb0bbcd5ed58805df857cf5e2e65b8c8326aa..513b41c5692ce9c0b6b826876c9f461a79b41ee7 100644 (file)
@@ -1,6 +1,3 @@
 <% content_for :extra_script do %>
-  <script>
-    window.sonarqube.urlRoot = window.baseUrl + '/profiles';
-  </script>
-  <script src="<%= ApplicationController.root_context -%>/js/bundles/quality-profiles.js?v=<%= sonar_version -%>"></script>
+  <script src="<%= ApplicationController.root_context -%>/js/bundles/app.js?v=<%= sonar_version -%>"></script>
 <% end %>
index 5cc9585f4dd8ff3c47740f46b7a4e7ccf0a89533..513b41c5692ce9c0b6b826876c9f461a79b41ee7 100644 (file)
@@ -1,6 +1,3 @@
 <% content_for :extra_script do %>
-  <script>
-    window.sonarqube.urlRoot = window.baseUrl + '/quality_gates';
-  </script>
-  <script src="<%= ApplicationController.root_context -%>/js/bundles/quality-gates.js?v=<%= sonar_version -%>"></script>
+  <script src="<%= ApplicationController.root_context -%>/js/bundles/app.js?v=<%= sonar_version -%>"></script>
 <% end %>