]> source.dussan.org Git - sonarqube.git/commitdiff
SONAR-8360 Add favorite projects filter
authorStas Vilchik <vilchiks@gmail.com>
Wed, 9 Nov 2016 09:04:13 +0000 (10:04 +0100)
committerStas Vilchik <vilchiks@gmail.com>
Wed, 9 Nov 2016 14:51:40 +0000 (15:51 +0100)
15 files changed:
server/sonar-web/src/main/js/apps/projects/components/AllProjects.js
server/sonar-web/src/main/js/apps/projects/components/AllProjectsContainer.js
server/sonar-web/src/main/js/apps/projects/components/FavoriteProjects.js [deleted file]
server/sonar-web/src/main/js/apps/projects/components/FavoriteProjectsContainer.js
server/sonar-web/src/main/js/apps/projects/components/PageSidebar.js
server/sonar-web/src/main/js/apps/projects/components/ProjectsList.js
server/sonar-web/src/main/js/apps/projects/components/ProjectsListFooterContainer.js
server/sonar-web/src/main/js/apps/projects/filters/CoverageFilter.js
server/sonar-web/src/main/js/apps/projects/filters/DuplicationsFilter.js
server/sonar-web/src/main/js/apps/projects/filters/FilterContainer.js
server/sonar-web/src/main/js/apps/projects/filters/IssuesFilter.js
server/sonar-web/src/main/js/apps/projects/filters/QualityGateFilter.js
server/sonar-web/src/main/js/apps/projects/filters/SizeFilter.js
server/sonar-web/src/main/js/apps/projects/store/actions.js
server/sonar-web/src/main/js/apps/projects/store/utils.js

index fbbfb11fd568b76a8f9dc0cc901d88f6fdf61dc0..cc303887e71d54838cb50e04372e4b6e407e1646 100644 (file)
@@ -21,12 +21,12 @@ import React from 'react';
 import ProjectsListContainer from './ProjectsListContainer';
 import ProjectsListFooterContainer from './ProjectsListFooterContainer';
 import PageSidebar from './PageSidebar';
-import NoProjects from './NoProjects';
 import { parseUrlQuery } from '../store/utils';
 
 export default class AllProjects extends React.Component {
   static propTypes = {
     user: React.PropTypes.oneOfType([React.PropTypes.object, React.PropTypes.bool]),
+    isFavorite: React.PropTypes.bool.isRequired,
     fetchProjects: React.PropTypes.func.isRequired
   };
 
@@ -47,7 +47,7 @@ export default class AllProjects extends React.Component {
   handleQueryChange () {
     const query = parseUrlQuery(this.props.location.query);
     this.setState({ query });
-    this.props.fetchProjects(query);
+    this.props.fetchProjects(query, this.props.isFavorite);
   }
 
   render () {
@@ -55,14 +55,17 @@ export default class AllProjects extends React.Component {
       return null;
     }
 
+    const favoriteAndNoFilters = this.props.isFavorite &&
+        !Object.keys(this.state.query).some(key => this.state.query[key] != null);
+
     return (
         <div className="page-with-sidebar projects-page">
           <div className="page-main">
-            <ProjectsListContainer noProjectsComponent={<NoProjects/>}/>
-            <ProjectsListFooterContainer query={this.state.query}/>
+            <ProjectsListContainer favoriteAndNoFilters={favoriteAndNoFilters}/>
+            <ProjectsListFooterContainer query={this.state.query} isFavorite={this.props.isFavorite}/>
           </div>
           <aside className="page-sidebar-fixed projects-sidebar">
-            <PageSidebar query={this.state.query}/>
+            <PageSidebar query={this.state.query} isFavorite={this.props.isFavorite}/>
           </aside>
         </div>
     );
index 5201152e943425a4ca3a65d7a86596ff1cc38056..d003248d72ca6f5bf885122faacf1aaf6ba5c59f 100644 (file)
@@ -23,7 +23,8 @@ import { fetchProjects } from '../store/actions';
 import { getCurrentUser } from '../../../app/store/rootReducer';
 
 const mapStateToProps = state => ({
-  user: getCurrentUser(state)
+  user: getCurrentUser(state),
+  isFavorite: false
 });
 
 export default connect(
diff --git a/server/sonar-web/src/main/js/apps/projects/components/FavoriteProjects.js b/server/sonar-web/src/main/js/apps/projects/components/FavoriteProjects.js
deleted file mode 100644 (file)
index 4c45ed6..0000000
+++ /dev/null
@@ -1,50 +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 ProjectsListContainer from './ProjectsListContainer';
-import NoFavoriteProjects from './NoFavoriteProjects';
-
-export default class FavoriteProjects extends React.Component {
-  static propTypes = {
-    user: React.PropTypes.object,
-    fetchFavoriteProjects: React.PropTypes.func.isRequired
-  };
-
-  componentDidMount () {
-    this.props.fetchFavoriteProjects();
-  }
-
-  render () {
-    if (!this.props.user) {
-      return null;
-    }
-
-    return (
-        <div className="page-with-sidebar">
-          <div className="page-main">
-            <div className="projects-list-container">
-              <ProjectsListContainer noProjectsComponent={<NoFavoriteProjects/>}/>
-            </div>
-          </div>
-          <aside className="page-sidebar-fixed projects-sidebar"/>
-        </div>
-    );
-  }
-}
index d78dafbcf42f2d4b50b80dba37faf7b5cd3a1b54..3e383549f3ef7180ab7fa15948dbf4e2ef847b49 100644 (file)
  * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
  */
 import { connect } from 'react-redux';
-import FavoriteProjects from './FavoriteProjects';
-import { fetchFavoriteProjects } from '../store/actions';
+import AllProjects from './AllProjects';
+import { fetchProjects } from '../store/actions';
 import { getCurrentUser } from '../../../app/store/rootReducer';
 
 const mapStateToProps = state => ({
-  user: getCurrentUser(state)
+  user: getCurrentUser(state),
+  isFavorite: true
 });
 
 export default connect(
     mapStateToProps,
-    { fetchFavoriteProjects }
-)(FavoriteProjects);
+    { fetchProjects }
+)(AllProjects);
index 2f44ca9798e18b45df467acd74b591cd177ae9be..811330c3a55c6ad18ee785703a51a6a656e1e0b4 100644 (file)
@@ -30,18 +30,21 @@ import { translate } from '../../../helpers/l10n';
 
 export default class PageSidebar extends React.Component {
   static propTypes = {
-    query: React.PropTypes.object.isRequired
+    query: React.PropTypes.object.isRequired,
+    isFavorite: React.PropTypes.bool.isRequired
   };
 
   render () {
     const isFiltered = Object.keys(this.props.query).some(key => this.props.query[key] != null);
 
+    const pathname = this.props.isFavorite ? '/projects/favorite' : '/projects';
+
     return (
         <div className="search-navigator-facets-list">
           <div className="projects-facets-header clearfix">
             {isFiltered && (
                 <div className="projects-facets-reset">
-                  <Link to="/projects" className="button button-red">
+                  <Link to={pathname} className="button button-red">
                     {translate('projects.clear_all_filters')}
                   </Link>
                 </div>
@@ -50,13 +53,13 @@ export default class PageSidebar extends React.Component {
             <h3>{translate('filters')}</h3>
           </div>
 
-          <QualityGateFilter query={this.props.query}/>
-          <ReliabilityFilter query={this.props.query}/>
-          <SecurityFilter query={this.props.query}/>
-          <MaintainabilityFilter query={this.props.query}/>
-          <CoverageFilter query={this.props.query}/>
-          <DuplicationsFilter query={this.props.query}/>
-          <SizeFilter query={this.props.query}/>
+          <QualityGateFilter query={this.props.query} isFavorite={this.props.isFavorite}/>
+          <ReliabilityFilter query={this.props.query} isFavorite={this.props.isFavorite}/>
+          <SecurityFilter query={this.props.query} isFavorite={this.props.isFavorite}/>
+          <MaintainabilityFilter query={this.props.query} isFavorite={this.props.isFavorite}/>
+          <CoverageFilter query={this.props.query} isFavorite={this.props.isFavorite}/>
+          <DuplicationsFilter query={this.props.query} isFavorite={this.props.isFavorite}/>
+          <SizeFilter query={this.props.query} isFavorite={this.props.isFavorite}/>
         </div>
     );
   }
index 53e20cbd6155519abe1369d50239a281d2ea6886..76274465b25f43b180bd96787740e8283378930e 100644 (file)
  */
 import React from 'react';
 import ProjectCardContainer from './ProjectCardContainer';
+import NoProjects from './NoProjects';
+import NoFavoriteProjects from './NoFavoriteProjects';
 
 export default class ProjectsList extends React.Component {
   static propTypes = {
     projects: React.PropTypes.arrayOf(React.PropTypes.string),
-    noProjectsComponent: React.PropTypes.element.isRequired
+    favoriteAndNoFilters: React.PropTypes.bool.isRequired
   };
 
+  renderNoProjects () {
+    return this.props.favoriteAndNoFilters ? (
+        <NoFavoriteProjects/>
+    ) : (
+        <NoProjects/>
+    );
+  }
+
   render () {
     const { projects } = this.props;
 
@@ -40,7 +50,7 @@ export default class ProjectsList extends React.Component {
                   <ProjectCardContainer key={projectKey} projectKey={projectKey}/>
               ))
           ) : (
-              this.props.noProjectsComponent
+              this.renderNoProjects()
           )}
         </div>
     );
index d30f699c7232ba5bc40a6781f433ca5fe4a28edb..210b4312b9c325af0954f925a556f1eda27460c3 100644 (file)
@@ -33,7 +33,7 @@ const mapStateToProps = state => {
 };
 
 const mapDispatchToProps = (dispatch, ownProps) => ({
-  loadMore: () => dispatch(fetchMoreProjects(ownProps.query))
+  loadMore: () => dispatch(fetchMoreProjects(ownProps.query, ownProps.isFavorite))
 });
 
 export default connect(
index ae0a6ac54d55c68fa9b4fd24d70ec92e6e1bd248..04cdd0e6f28e8840fd686cfb13a63dbf2a2f6fc8 100644 (file)
@@ -47,7 +47,8 @@ export default class CoverageFilter extends React.Component {
             renderName={() => 'Coverage'}
             renderOption={this.renderOption}
             getFacetValueForOption={this.getFacetValueForOption}
-            query={this.props.query}/>
+            query={this.props.query}
+            isFavorite={this.props.isFavorite}/>
     );
   }
 }
index 18be81714edb82a4191633129910bae06df8d63d..2c3788bb0b3f476370eb72c15dda0af74544763b 100644 (file)
@@ -47,7 +47,8 @@ export default class DuplicationsFilter extends React.Component {
             renderName={() => 'Duplications'}
             renderOption={this.renderOption}
             getFacetValueForOption={this.getFacetValueForOption}
-            query={this.props.query}/>
+            query={this.props.query}
+            isFavorite={this.props.isFavorite}/>
     );
   }
 }
index 7f030717c34d5047a7b6f42c4e66b1e9dff23ff0..e943a6b2847b87cecf31ceb19eb68b1528982327 100644 (file)
@@ -29,8 +29,9 @@ const mapStateToProps = (state, ownProps) => ({
   facet: getProjectsAppFacetByProperty(state, ownProps.property),
   maxFacetValue: getProjectsAppMaxFacetValue(state),
   getFilterUrl: part => {
+    const pathname = ownProps.isFavorite ? '/projects/favorite': '/projects';
     const query = omitBy({ ...ownProps.query, ...part }, isNil);
-    return { pathname: '/projects', query };
+    return { pathname, query };
   }
 });
 
index f3d76b6c6ae42ae7a85c5cc91bd2142b17fb663b..e66902fc7766e9cd6bb9fa86073089456ac612bb 100644 (file)
@@ -45,7 +45,8 @@ export default class IssuesFilter extends React.Component {
             renderName={() => this.props.name}
             renderOption={this.renderOption}
             getFacetValueForOption={this.getFacetValueForOption}
-            query={this.props.query}/>
+            query={this.props.query}
+            isFavorite={this.props.isFavorite}/>
     );
   }
 }
index e18f964a4728cdfdd12b16897aca8fb6ffde8d0c..cd9139af45cb7d10948171278cd224c4f829aeac 100644 (file)
@@ -40,7 +40,8 @@ export default class QualityGateFilter extends React.Component {
             renderName={() => 'Quality Gate'}
             renderOption={this.renderOption}
             getFacetValueForOption={this.getFacetValueForOption}
-            query={this.props.query}/>
+            query={this.props.query}
+            isFavorite={this.props.isFavorite}/>
     );
   }
 }
index 6ad997e09d563311d8906313f6a95b1d37b990a1..48670902644a7bcfb46e86d496ff72a9d78301f8 100644 (file)
@@ -47,7 +47,8 @@ export default class SizeFilter extends React.Component {
             renderName={() => 'Size'}
             renderOption={this.renderOption}
             getFacetValueForOption={this.getFacetValueForOption}
-            query={this.props.query}/>
+            query={this.props.query}
+            isFavorite={this.props.isFavorite}/>
     );
   }
 }
index 922400ead5993488d7e7dbe83b9ace91d4a3cadc..76c3cf13210896fa0bfdc28f9c14282e340d95c4 100644 (file)
@@ -113,22 +113,22 @@ const onReceiveMoreProjects = dispatch => response => {
   dispatch(updateState({ pageIndex: response.paging.pageIndex }));
 };
 
-export const fetchProjects = query => dispatch => {
+export const fetchProjects = (query, isFavorite) => dispatch => {
   dispatch(updateState({ loading: true }));
   const data = { ps: PAGE_SIZE, facets: FACETS.join() };
-  const filter = convertToFilter(query);
+  const filter = convertToFilter(query, isFavorite);
   if (filter) {
     data.filter = filter;
   }
   return searchProjects(data).then(onReceiveProjects(dispatch), onFail(dispatch));
 };
 
-export const fetchMoreProjects = query => (dispatch, getState) => {
+export const fetchMoreProjects = (query, isFavorite) => (dispatch, getState) => {
   dispatch(updateState({ loading: true }));
   const state = getState();
   const { pageIndex } = getProjectsAppState(state);
   const data = { ps: PAGE_SIZE, p: pageIndex + 1 };
-  const filter = convertToFilter(query);
+  const filter = convertToFilter(query, isFavorite);
   if (filter) {
     data.filter = filter;
   }
index 9ed9dd74ef4ca29e11a76054fb0903fbfd2a345c..37c42077d5dc8480e24ba6b16bf456d6035a35d7 100644 (file)
@@ -101,9 +101,13 @@ const convertSize = size => {
   }
 };
 
-export const convertToFilter = query => {
+export const convertToFilter = (query, isFavorite) => {
   const conditions = [];
 
+  if (isFavorite) {
+    conditions.push('isFavorite');
+  }
+
   if (query['gate'] != null) {
     conditions.push('alert_status = ' + query['gate']);
   }