]> source.dussan.org Git - sonarqube.git/commitdiff
SONAR-8300 Create new "Projects" page [5th iteration] (#1357)
authorStas Vilchik <vilchiks@gmail.com>
Fri, 4 Nov 2016 10:36:54 +0000 (11:36 +0100)
committerGitHub <noreply@github.com>
Fri, 4 Nov 2016 10:36:54 +0000 (11:36 +0100)
23 files changed:
server/sonar-web/src/main/js/apps/projects/components/AllProjects.js [new file with mode: 0644]
server/sonar-web/src/main/js/apps/projects/components/AllProjectsContainer.js [new file with mode: 0644]
server/sonar-web/src/main/js/apps/projects/components/App.js
server/sonar-web/src/main/js/apps/projects/components/AppContainer.js [deleted file]
server/sonar-web/src/main/js/apps/projects/components/FavoriteFilter.js
server/sonar-web/src/main/js/apps/projects/components/FavoriteProjects.js
server/sonar-web/src/main/js/apps/projects/components/PageHeader.js
server/sonar-web/src/main/js/apps/projects/components/PageSidebar.js
server/sonar-web/src/main/js/apps/projects/components/ProjectsListHeader.js [deleted file]
server/sonar-web/src/main/js/apps/projects/components/ProjectsListHeaderContainer.js [deleted file]
server/sonar-web/src/main/js/apps/projects/routes.js
server/sonar-web/src/main/js/apps/projects/store/actions.js
server/sonar-web/src/main/js/apps/projects/store/facets/reducer.js [deleted file]
server/sonar-web/src/main/js/apps/projects/store/facetsDuck.js [new file with mode: 0644]
server/sonar-web/src/main/js/apps/projects/store/projects/actions.js [deleted file]
server/sonar-web/src/main/js/apps/projects/store/projects/reducer.js [deleted file]
server/sonar-web/src/main/js/apps/projects/store/projectsDuck.js [new file with mode: 0644]
server/sonar-web/src/main/js/apps/projects/store/reducer.js
server/sonar-web/src/main/js/apps/projects/store/state/actions.js [deleted file]
server/sonar-web/src/main/js/apps/projects/store/state/reducer.js [deleted file]
server/sonar-web/src/main/js/apps/projects/store/stateDuck.js [new file with mode: 0644]
server/sonar-web/src/main/js/apps/projects/styles.css
sonar-core/src/main/resources/org/sonar/l10n/core.properties

diff --git a/server/sonar-web/src/main/js/apps/projects/components/AllProjects.js b/server/sonar-web/src/main/js/apps/projects/components/AllProjects.js
new file mode 100644 (file)
index 0000000..2e49235
--- /dev/null
@@ -0,0 +1,70 @@
+/*
+ * 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 ProjectsListContainer from './ProjectsListContainer';
+import ProjectsListFooterContainer from './ProjectsListFooterContainer';
+import PageSidebar from './PageSidebar';
+import { parseUrlQuery } from '../store/utils';
+import '../styles.css';
+
+export default class AllProjects extends React.Component {
+  static propTypes = {
+    user: React.PropTypes.oneOfType([React.PropTypes.object, React.PropTypes.bool]),
+    fetchProjects: React.PropTypes.func.isRequired
+  };
+
+  state = {
+    query: {}
+  };
+
+  componentDidMount () {
+    this.handleQueryChange();
+  }
+
+  componentDidUpdate (prevProps) {
+    if (prevProps.location.query !== this.props.location.query) {
+      this.handleQueryChange();
+    }
+  }
+
+  handleQueryChange () {
+    const query = parseUrlQuery(this.props.location.query);
+    this.setState({ query });
+    this.props.fetchProjects(query);
+  }
+
+  render () {
+    if (this.props.user == null) {
+      return null;
+    }
+
+    return (
+        <div className="page-with-sidebar projects-page">
+          <div className="page-main">
+            <ProjectsListContainer/>
+            <ProjectsListFooterContainer query={this.state.query}/>
+          </div>
+          <aside className="page-sidebar-fixed projects-sidebar">
+            <PageSidebar query={this.state.query}/>
+          </aside>
+        </div>
+    );
+  }
+}
diff --git a/server/sonar-web/src/main/js/apps/projects/components/AllProjectsContainer.js b/server/sonar-web/src/main/js/apps/projects/components/AllProjectsContainer.js
new file mode 100644 (file)
index 0000000..5201152
--- /dev/null
@@ -0,0 +1,32 @@
+/*
+ * 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 AllProjects from './AllProjects';
+import { fetchProjects } from '../store/actions';
+import { getCurrentUser } from '../../../app/store/rootReducer';
+
+const mapStateToProps = state => ({
+  user: getCurrentUser(state)
+});
+
+export default connect(
+    mapStateToProps,
+    { fetchProjects }
+)(AllProjects);
index 665683cd689285d71d3d5f0ba57d6de5d175c878..3bc99f5782be260b43c3f1e185cbb3cfd42a8ec8 100644 (file)
 import React from 'react';
 import Helmet from 'react-helmet';
 import PageHeaderContainer from './PageHeaderContainer';
-import ProjectsListContainer from './ProjectsListContainer';
-import ProjectsListFooterContainer from './ProjectsListFooterContainer';
-import PageSidebar from './PageSidebar';
-import ProjectsListHeaderContainer from './ProjectsListHeaderContainer';
 import GlobalMessagesContainer from '../../../app/components/GlobalMessagesContainer';
-import { parseUrlQuery } from '../store/utils';
 import { translate } from '../../../helpers/l10n';
 import '../styles.css';
 
 export default class App extends React.Component {
-  static propTypes = {
-    user: React.PropTypes.oneOfType([React.PropTypes.object, React.PropTypes.bool]),
-    fetchProjects: React.PropTypes.func.isRequired
-  };
-
-  state = {
-    query: {}
-  };
-
   componentDidMount () {
     document.querySelector('html').classList.add('dashboard-page');
-    this.handleQueryChange();
-  }
-
-  componentDidUpdate (prevProps) {
-    if (prevProps.location.query !== this.props.location.query) {
-      this.handleQueryChange();
-    }
   }
 
   componentWillUnmount () {
     document.querySelector('html').classList.remove('dashboard-page');
   }
 
-  handleQueryChange () {
-    const query = parseUrlQuery(this.props.location.query);
-    this.setState({ query });
-    this.props.fetchProjects(query);
-  }
-
   render () {
-    if (this.props.user == null) {
-      return null;
-    }
-
     return (
         <div id="projects-page" className="page page-limited">
-          <Helmet title={translate('projects.page')} titleTemplate="SonarQube - %s"/>
+          <Helmet title={translate('projects.page')} titleTemplate="%s - SonarQube"/>
 
           <GlobalMessagesContainer/>
 
-          <div className="page-with-sidebar page-with-left-sidebar">
+          <div className="page-with-sidebar">
             <div className="page-main">
-              <div className="projects-list-container">
-                <ProjectsListHeaderContainer/>
-                <ProjectsListContainer/>
-                <ProjectsListFooterContainer query={this.state.query}/>
-              </div>
-            </div>
-            <aside className="page-sidebar-fixed projects-sidebar">
               <PageHeaderContainer/>
-              <PageSidebar query={this.state.query}/>
-            </aside>
+            </div>
+            <aside className="page-sidebar-fixed projects-sidebar"/>
           </div>
+
+          {this.props.children}
         </div>
     );
   }
diff --git a/server/sonar-web/src/main/js/apps/projects/components/AppContainer.js b/server/sonar-web/src/main/js/apps/projects/components/AppContainer.js
deleted file mode 100644 (file)
index 12a9cd3..0000000
+++ /dev/null
@@ -1,30 +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 { connect } from 'react-redux';
-import App from './App';
-import { fetchProjects } from '../store/actions';
-import { getCurrentUser } from '../../../app/store/rootReducer';
-
-export default connect(
-    state => ({
-      user: getCurrentUser(state)
-    }),
-    { fetchProjects }
-)(App);
index a2355a8ef4e0d8a182304e298ebe44690bb4f847..3e665bd47a09da85db18d91b5df9ea0567be8a3f 100644 (file)
@@ -19,6 +19,7 @@
  */
 import React from 'react';
 import { IndexLink, Link } from 'react-router';
+import { translate } from '../../../helpers/l10n';
 
 export default class FavoriteFilter extends React.Component {
   render () {
@@ -27,11 +28,14 @@ export default class FavoriteFilter extends React.Component {
     }
 
     return (
-        <div>
-          <span className="note spacer-right">Quick Filters:</span>
+        <div className="pull-left big-spacer-left">
           <div className="button-group">
-            <IndexLink to="/projects" className="button" activeClassName="button-active">All</IndexLink>
-            <Link to="/projects/favorite" className="button" activeClassName="button-active">Favorite</Link>
+            <Link to="/projects/favorite" className="button" activeClassName="button-active">
+              {translate('favorite')}
+            </Link>
+            <IndexLink to="/projects" className="button" activeClassName="button-active">
+              {translate('all')}
+            </IndexLink>
           </div>
         </div>
     );
index 2e1410d8e0c0d276bca7c9f4dac0ed80782176ce..22fd723958b49ba2671af32392aff61b0093288e 100644 (file)
  * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
  */
 import React from 'react';
-import Helmet from 'react-helmet';
-import PageHeaderContainer from './PageHeaderContainer';
 import ProjectsListContainer from './ProjectsListContainer';
-import ProjectsListHeaderContainer from './ProjectsListHeaderContainer';
-import GlobalMessagesContainer from '../../../app/components/GlobalMessagesContainer';
-import { translate } from '../../../helpers/l10n';
 import '../styles.css';
 
 export default class FavoriteProjects extends React.Component {
@@ -33,42 +28,22 @@ export default class FavoriteProjects extends React.Component {
   };
 
   componentDidMount () {
-    document.querySelector('html').classList.add('dashboard-page');
     this.props.fetchFavoriteProjects();
   }
 
-  componentWillUnmount () {
-    document.querySelector('html').classList.remove('dashboard-page');
-  }
-
   render () {
     if (!this.props.user) {
       return null;
     }
 
     return (
-        <div id="projects-page" className="page page-limited">
-          <Helmet title={translate('projects.page')} titleTemplate="SonarQube - %s"/>
-
-          <GlobalMessagesContainer/>
-
-          <div className="page-with-sidebar page-with-left-sidebar">
-            <div className="page-main">
-              <div className="projects-list-container">
-                <ProjectsListHeaderContainer/>
-                <ProjectsListContainer/>
-              </div>
+        <div className="page-with-sidebar">
+          <div className="page-main">
+            <div className="projects-list-container">
+              <ProjectsListContainer/>
             </div>
-            <aside className="page-sidebar-fixed projects-sidebar">
-              <PageHeaderContainer/>
-              <div className="search-navigator-facets-list">
-                <div className="projects-facets-header">
-                  <h3>Filters</h3>
-                </div>
-                <p className="note text-center">Filters are not available.</p>
-              </div>
-            </aside>
           </div>
+          <aside className="page-sidebar-fixed projects-sidebar"/>
         </div>
     );
   }
index 9ffdb687169fcc2b7d756e1dc1b186a3b9253aa5..346ed06e7e462e635ca20382a0d29ab1d23227f0 100644 (file)
  * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
  */
 import React from 'react';
+import FavoriteFilterContainer from './FavoriteFilterContainer';
 import { translate } from '../../../helpers/l10n';
 
 export default class PageHeader extends React.Component {
   static propTypes = {
-    loading: React.PropTypes.bool
+    loading: React.PropTypes.bool,
+    total: React.PropTypes.number
   };
 
   render () {
@@ -30,11 +32,21 @@ export default class PageHeader extends React.Component {
 
     return (
         <header className="page-header">
+          <div className="page-actions">
+            {!!loading && (
+                <i className="spinner spacer-right"/>
+            )}
+
+            {this.props.total != null && (
+                <span>
+                <strong>{this.props.total}</strong> {translate('projects._projects')}
+              </span>
+            )}
+          </div>
+
           <h1 className="page-title">{translate('projects.page')}</h1>
 
-          {!!loading && (
-              <i className="spinner"/>
-          )}
+          <FavoriteFilterContainer/>
         </header>
     );
   }
index 087f1cc7904dca8ff6f3b1f004f4cd5f9551defd..2f44ca9798e18b45df467acd74b591cd177ae9be 100644 (file)
@@ -26,6 +26,7 @@ import QualityGateFilter from '../filters/QualityGateFilter';
 import ReliabilityFilter from '../filters/ReliabilityFilter';
 import SecurityFilter from '../filters/SecurityFilter';
 import MaintainabilityFilter from '../filters/MaintainabilityFilter';
+import { translate } from '../../../helpers/l10n';
 
 export default class PageSidebar extends React.Component {
   static propTypes = {
@@ -41,12 +42,12 @@ export default class PageSidebar extends React.Component {
             {isFiltered && (
                 <div className="projects-facets-reset">
                   <Link to="/projects" className="button button-red">
-                    Clear All Filters
+                    {translate('projects.clear_all_filters')}
                   </Link>
                 </div>
             )}
 
-            <h3>Filters</h3>
+            <h3>{translate('filters')}</h3>
           </div>
 
           <QualityGateFilter query={this.props.query}/>
diff --git a/server/sonar-web/src/main/js/apps/projects/components/ProjectsListHeader.js b/server/sonar-web/src/main/js/apps/projects/components/ProjectsListHeader.js
deleted file mode 100644 (file)
index 518bcb0..0000000
+++ /dev/null
@@ -1,43 +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 FavoriteFilterContainer from './FavoriteFilterContainer';
-import { translate } from '../../../helpers/l10n';
-
-export default class ProjectsListHeader extends React.Component {
-  static propTypes = {
-    total: React.PropTypes.number
-  };
-
-  render () {
-    return (
-        <header className="page-header">
-          <div className="page-actions">
-            {this.props.total != null && (
-              <span>
-                <strong>{this.props.total}</strong> {translate('projects._projects')}
-              </span>
-            )}
-          </div>
-          <FavoriteFilterContainer/>
-        </header>
-    );
-  }
-}
diff --git a/server/sonar-web/src/main/js/apps/projects/components/ProjectsListHeaderContainer.js b/server/sonar-web/src/main/js/apps/projects/components/ProjectsListHeaderContainer.js
deleted file mode 100644 (file)
index f21a819..0000000
+++ /dev/null
@@ -1,30 +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 { connect } from 'react-redux';
-import ProjectsListHeader from './ProjectsListHeader';
-import { getProjectsAppState } from '../../../app/store/rootReducer';
-
-const mapStateToProps = state => (
-    getProjectsAppState(state)
-);
-
-export default connect(
-    mapStateToProps
-)(ProjectsListHeader);
index cc1ecd10506775d07631fa20a5beb0d514f32fb1..114e82ca1976007359767a26e43633392479f531 100644 (file)
  */
 import React from 'react';
 import { Route, IndexRoute } from 'react-router';
-import AppContainer from './components/AppContainer';
+import App from './components/App';
+import AllProjectsContainer from './components/AllProjectsContainer';
 import FavoriteProjectsContainer from './components/FavoriteProjectsContainer';
 
 export default (
-    <Route path="projects">
-      <IndexRoute component={AppContainer}/>
+    <Route path="projects" component={App}>
+      <IndexRoute component={AllProjectsContainer}/>
       <Route path="favorite" component={FavoriteProjectsContainer}/>
     </Route>
 );
index 003cdceba64e7f9f6e4bbb6670eb5fd9e3aa599f..561937468f326a1855f77b805bcc25e62f5ce085 100644 (file)
@@ -22,8 +22,8 @@ import { searchProjects } from '../../../api/components';
 import { addGlobalErrorMessage } from '../../../components/store/globalMessages';
 import { parseError } from '../../code/utils';
 import { receiveComponents } from '../../../app/store/components/actions';
-import { receiveProjects, receiveMoreProjects } from './projects/actions';
-import { updateState } from './state/actions';
+import { receiveProjects, receiveMoreProjects } from './projectsDuck';
+import { updateState } from './stateDuck';
 import { getProjectsAppState } from '../../../app/store/rootReducer';
 import { getMeasuresForProjects } from '../../../api/measures';
 import { receiveComponentsMeasures } from '../../../app/store/measures/actions';
diff --git a/server/sonar-web/src/main/js/apps/projects/store/facets/reducer.js b/server/sonar-web/src/main/js/apps/projects/store/facets/reducer.js
deleted file mode 100644 (file)
index 802e8d3..0000000
+++ /dev/null
@@ -1,87 +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 flatMap from 'lodash/flatMap';
-import sumBy from 'lodash/sumBy';
-import { createMap } from '../../../../components/store/generalReducers';
-import { RECEIVE_PROJECTS } from '../projects/actions';
-import { mapMetricToProperty } from '../utils';
-
-const CUMULATIVE_FACETS = [
-  'reliability',
-  'security',
-  'maintainability',
-  'coverage',
-  'duplications',
-  'size'
-];
-
-const REVERSED_FACETS = [
-  'coverage'
-];
-
-const mapFacetValues = values => {
-  const map = {};
-  values.forEach(value => {
-    map[value.val] = value.count;
-  });
-  return map;
-};
-
-const cumulativeMapFacetValues = values => {
-  const map = {};
-  let sum = sumBy(values, value => value.count);
-  values.forEach((value, index) => {
-    map[value.val] = index > 0 && index < values.length - 1 ? sum : value.count;
-    sum -= value.count;
-  });
-  return map;
-};
-
-const getFacetsMap = facets => {
-  const map = {};
-  facets.forEach(facet => {
-    const property = mapMetricToProperty(facet.property);
-    const { values } = facet;
-    if (REVERSED_FACETS.includes(property)) {
-      values.reverse();
-    }
-    map[property] = CUMULATIVE_FACETS.includes(property) ?
-        cumulativeMapFacetValues(values) :
-        mapFacetValues(values);
-  });
-  return map;
-};
-
-const reducer = createMap(
-    (state, action) => action.type === RECEIVE_PROJECTS,
-    () => false,
-    (state, action) => getFacetsMap(action.facets)
-);
-
-export default reducer;
-
-export const getFacetByProperty = (state, property) => (
-    state[property]
-);
-
-export const getMaxFacetValue = state => {
-  const allValues = flatMap(Object.values(state), facet => Object.values(facet));
-  return Math.max.apply(null, allValues);
-};
diff --git a/server/sonar-web/src/main/js/apps/projects/store/facetsDuck.js b/server/sonar-web/src/main/js/apps/projects/store/facetsDuck.js
new file mode 100644 (file)
index 0000000..9a12aa6
--- /dev/null
@@ -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 flatMap from 'lodash/flatMap';
+import sumBy from 'lodash/sumBy';
+import { createMap } from '../../../components/store/generalReducers';
+import { actions } from './projectsDuck';
+import { mapMetricToProperty } from './utils';
+
+const CUMULATIVE_FACETS = [
+  'reliability',
+  'security',
+  'maintainability',
+  'coverage',
+  'duplications',
+  'size'
+];
+
+const REVERSED_FACETS = [
+  'coverage'
+];
+
+const mapFacetValues = values => {
+  const map = {};
+  values.forEach(value => {
+    map[value.val] = value.count;
+  });
+  return map;
+};
+
+const cumulativeMapFacetValues = values => {
+  const map = {};
+  let sum = sumBy(values, value => value.count);
+  values.forEach((value, index) => {
+    map[value.val] = index > 0 && index < values.length - 1 ? sum : value.count;
+    sum -= value.count;
+  });
+  return map;
+};
+
+const getFacetsMap = facets => {
+  const map = {};
+  facets.forEach(facet => {
+    const property = mapMetricToProperty(facet.property);
+    const { values } = facet;
+    if (REVERSED_FACETS.includes(property)) {
+      values.reverse();
+    }
+    map[property] = CUMULATIVE_FACETS.includes(property) ?
+        cumulativeMapFacetValues(values) :
+        mapFacetValues(values);
+  });
+  return map;
+};
+
+const reducer = createMap(
+    (state, action) => action.type === actions.RECEIVE_PROJECTS,
+    () => false,
+    (state, action) => getFacetsMap(action.facets)
+);
+
+export default reducer;
+
+export const getFacetByProperty = (state, property) => (
+    state[property]
+);
+
+export const getMaxFacetValue = state => {
+  const allValues = flatMap(Object.values(state), facet => Object.values(facet));
+  return Math.max.apply(null, allValues);
+};
diff --git a/server/sonar-web/src/main/js/apps/projects/store/projects/actions.js b/server/sonar-web/src/main/js/apps/projects/store/projects/actions.js
deleted file mode 100644 (file)
index ed411c0..0000000
+++ /dev/null
@@ -1,33 +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.
- */
-export const RECEIVE_PROJECTS = 'projects/RECEIVE_PROJECTS';
-
-export const receiveProjects = (projects, facets) => ({
-  type: RECEIVE_PROJECTS,
-  projects,
-  facets
-});
-
-export const RECEIVE_MORE_PROJECTS = 'projects/RECEIVE_MORE_PROJECTS';
-
-export const receiveMoreProjects = projects => ({
-  type: RECEIVE_MORE_PROJECTS,
-  projects
-});
diff --git a/server/sonar-web/src/main/js/apps/projects/store/projects/reducer.js b/server/sonar-web/src/main/js/apps/projects/store/projects/reducer.js
deleted file mode 100644 (file)
index d5669ad..0000000
+++ /dev/null
@@ -1,37 +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 { RECEIVE_PROJECTS, RECEIVE_MORE_PROJECTS } from './actions';
-
-const reducer = (state = null, action = {}) => {
-  if (action.type === RECEIVE_PROJECTS) {
-    return action.projects.map(project => project.key);
-  }
-
-  if (action.type === RECEIVE_MORE_PROJECTS) {
-    const keys = action.projects.map(project => project.key);
-    return state != null ? [...state, ...keys] : keys;
-  }
-
-  return state;
-};
-
-export default reducer;
-
-export const getProjects = state => state;
diff --git a/server/sonar-web/src/main/js/apps/projects/store/projectsDuck.js b/server/sonar-web/src/main/js/apps/projects/store/projectsDuck.js
new file mode 100644 (file)
index 0000000..a0699c3
--- /dev/null
@@ -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 const actions = {
+  RECEIVE_PROJECTS: 'projects/RECEIVE_PROJECTS',
+  RECEIVE_MORE_PROJECTS: 'projects/RECEIVE_MORE_PROJECTS'
+};
+
+export const receiveProjects = (projects, facets) => ({
+  type: actions.RECEIVE_PROJECTS,
+  projects,
+  facets
+});
+
+export const receiveMoreProjects = projects => ({
+  type: actions.RECEIVE_MORE_PROJECTS,
+  projects
+});
+
+const reducer = (state = null, action = {}) => {
+  if (action.type === actions.RECEIVE_PROJECTS) {
+    return action.projects.map(project => project.key);
+  }
+
+  if (action.type === actions.RECEIVE_MORE_PROJECTS) {
+    const keys = action.projects.map(project => project.key);
+    return state != null ? [...state, ...keys] : keys;
+  }
+
+  return state;
+};
+
+export default reducer;
+
+export const getProjects = state => state;
index 1e2487aa794fd205f3f519d4b5c285c7c150a0db..14930279ac164b7bb5f28722e657370088105c30 100644 (file)
@@ -18,9 +18,9 @@
  * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
  */
 import { combineReducers } from 'redux';
-import projects, * as fromProjects from './projects/reducer';
-import state from './state/reducer';
-import facets, * as fromFacets from './facets/reducer';
+import projects, * as fromProjects from './projectsDuck';
+import state from './stateDuck';
+import facets, * as fromFacets from './facetsDuck';
 
 export default combineReducers({ projects, state, facets });
 
diff --git a/server/sonar-web/src/main/js/apps/projects/store/state/actions.js b/server/sonar-web/src/main/js/apps/projects/store/state/actions.js
deleted file mode 100644 (file)
index 7046e08..0000000
+++ /dev/null
@@ -1,25 +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.
- */
-export const UPDATE_STATE = 'projects/UPDATE_STATE';
-
-export const updateState = changes => ({
-  type: UPDATE_STATE,
-  changes
-});
diff --git a/server/sonar-web/src/main/js/apps/projects/store/state/reducer.js b/server/sonar-web/src/main/js/apps/projects/store/state/reducer.js
deleted file mode 100644 (file)
index 853accd..0000000
+++ /dev/null
@@ -1,30 +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 { UPDATE_STATE } from './actions';
-
-const reducer = (state = {}, action = {}) => {
-  if (action.type === UPDATE_STATE) {
-    return { ...state, ...action.changes };
-  }
-
-  return state;
-};
-
-export default reducer;
diff --git a/server/sonar-web/src/main/js/apps/projects/store/stateDuck.js b/server/sonar-web/src/main/js/apps/projects/store/stateDuck.js
new file mode 100644 (file)
index 0000000..d74a8f9
--- /dev/null
@@ -0,0 +1,43 @@
+/*
+ * 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 { createValue } from '../../../components/store/generalReducers';
+
+export const actions = {
+  UPDATE_STATE: 'projects/UPDATE_STATE'
+};
+
+export const updateState = changes => ({
+  type: actions.UPDATE_STATE,
+  changes
+});
+
+export default createValue(
+    // should update
+    (state, action) => action.type === actions.UPDATE_STATE,
+
+    // should reset
+    () => false,
+
+    // get next value
+    (state, action) => ({ ...state, ...action.changes }),
+
+    // default value
+    {}
+);
index 15522753d8302990720c4e35775267503b30944e..a71aceb70d25bf007c21fecc183311245200a047 100644 (file)
@@ -2,10 +2,6 @@
   width: 260px;
 }
 
-.projects-list-container {
-  width: 740px;
-}
-
 .projects-list .page-actions {
   margin-bottom: 0;
 }
index 5dde6db09202351626403910e17429c0f72fcc5c..f86e5d0da2e96cc32b63ed4eb7e32178e2384786 100644 (file)
@@ -69,6 +69,7 @@ false=False
 favorite=Favorite
 file=File
 files=Files
+filters=Filters
 filter_verb=Filter
 follow=Follow
 global=Global
@@ -888,6 +889,7 @@ projects.page.description=Explore projects by coverage, duplications or size.
 projects._projects=projects
 projects.no_projects.1=We couldn't find any projects matching selected criteria.
 projects.no_projects.2=Try to change filters to get some results.
+projects.clear_all_filters=Clear All Filters
 
 
 #------------------------------------------------------------------------------