]> source.dussan.org Git - sonarqube.git/commitdiff
SONAR-8300 display favorite projects
authorStas Vilchik <vilchiks@gmail.com>
Tue, 1 Nov 2016 13:58:49 +0000 (14:58 +0100)
committerStas Vilchik <vilchiks@gmail.com>
Wed, 2 Nov 2016 08:03:26 +0000 (09:03 +0100)
12 files changed:
server/sonar-web/src/main/js/app/store/users/reducer.js
server/sonar-web/src/main/js/apps/projects/components/App.js
server/sonar-web/src/main/js/apps/projects/components/AppContainer.js
server/sonar-web/src/main/js/apps/projects/components/FavoriteFilter.js [new file with mode: 0644]
server/sonar-web/src/main/js/apps/projects/components/FavoriteFilterContainer.js [new file with mode: 0644]
server/sonar-web/src/main/js/apps/projects/components/FavoriteProjects.js [new file with mode: 0644]
server/sonar-web/src/main/js/apps/projects/components/FavoriteProjectsContainer.js [new file with mode: 0644]
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/styles.css
server/sonar-web/src/main/less/init/forms.less
server/sonar-web/src/main/webapp/WEB-INF/app/controllers/projects_controller.rb

index dec079465f2a1bb4f60a272a85e86a4efaf096c9..d67d04bfdf7cb47056930671fbd415049e1eb773 100644 (file)
@@ -39,7 +39,7 @@ const userLogins = (state = [], action = {}) => {
 
 const currentUser = (state = null, action = {}) => {
   if (action.type === RECEIVE_CURRENT_USER) {
-    return action.user.login;
+    return action.user.isLoggedIn ? action.user.login : false;
   }
 
   return state;
@@ -48,5 +48,5 @@ const currentUser = (state = null, action = {}) => {
 export default combineReducers({ usersByLogin, userLogins, currentUser });
 
 export const getCurrentUser = state => (
-    state.currentUser ? state.usersByLogin[state.currentUser] : null
+    state.currentUser ? state.usersByLogin[state.currentUser] : state.currentUser
 );
index c86cd6935df44afde7d893a54bf518d0657686f6..34d280a228e366718eb7fc6cfaa43e338f01372c 100644 (file)
@@ -23,13 +23,15 @@ import PageHeaderContainer from './PageHeaderContainer';
 import ProjectsListContainer from './ProjectsListContainer';
 import ProjectsListFooterContainer from './ProjectsListFooterContainer';
 import PageSidebarContainer from './PageSidebarContainer';
+import FavoriteFilterContainer from './FavoriteFilterContainer';
 import GlobalMessagesContainer from '../../../app/components/GlobalMessagesContainer';
 import { parseUrlQuery } from '../store/utils';
-import '../styles.css';
 import { translate } from '../../../helpers/l10n';
+import '../styles.css';
 
 export default class App extends React.Component {
   static propTypes = {
+    user: React.PropTypes.object,
     fetchProjects: React.PropTypes.func.isRequired
   };
 
@@ -59,6 +61,10 @@ export default class App extends React.Component {
   }
 
   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"/>
@@ -73,6 +79,7 @@ export default class App extends React.Component {
               <ProjectsListFooterContainer query={this.state.query}/>
             </div>
             <aside className="page-sidebar-fixed">
+              <FavoriteFilterContainer/>
               <PageSidebarContainer query={this.state.query}/>
             </aside>
           </div>
index f2956e0b14a9666df79c46e69d45563855df2c53..12a9cd32bbbf5a30f88c2dbf998166f59129ce27 100644 (file)
 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);
diff --git a/server/sonar-web/src/main/js/apps/projects/components/FavoriteFilter.js b/server/sonar-web/src/main/js/apps/projects/components/FavoriteFilter.js
new file mode 100644 (file)
index 0000000..2ef4f12
--- /dev/null
@@ -0,0 +1,36 @@
+/*
+ * 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 { IndexLink, Link } from 'react-router';
+
+export default class FavoriteFilter extends React.Component {
+  render () {
+    if (!this.props.user) {
+      return null;
+    }
+
+    return (
+      <div className="button-group projects-favorite-filter">
+        <IndexLink to="/projects" className="button" activeClassName="button-active">All</IndexLink>
+        <Link to="/projects/favorite" className="button" activeClassName="button-active">Favorite</Link>
+      </div>
+    );
+  }
+}
diff --git a/server/sonar-web/src/main/js/apps/projects/components/FavoriteFilterContainer.js b/server/sonar-web/src/main/js/apps/projects/components/FavoriteFilterContainer.js
new file mode 100644 (file)
index 0000000..c6437cf
--- /dev/null
@@ -0,0 +1,30 @@
+/*
+ * 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 FavoriteFilter from './FavoriteFilter';
+import { getCurrentUser } from '../../../app/store/rootReducer';
+
+const mapStateToProps = state => ({
+  user: getCurrentUser(state)
+});
+
+export default connect(
+    mapStateToProps
+)(FavoriteFilter);
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
new file mode 100644 (file)
index 0000000..6503b1e
--- /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 Helmet from 'react-helmet';
+import PageHeaderContainer from './PageHeaderContainer';
+import ProjectsListContainer from './ProjectsListContainer';
+import FavoriteFilterContainer from './FavoriteFilterContainer';
+import GlobalMessagesContainer from '../../../app/components/GlobalMessagesContainer';
+import { translate } from '../../../helpers/l10n';
+import '../styles.css';
+
+export default class FavoriteProjects extends React.Component {
+  static propTypes = {
+    user: React.PropTypes.object,
+    fetchFavoriteProjects: React.PropTypes.func.isRequired
+  };
+
+  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"/>
+
+          <PageHeaderContainer/>
+
+          <GlobalMessagesContainer/>
+
+          <div className="page-with-sidebar page-with-left-sidebar">
+            <div className="page-main">
+              <ProjectsListContainer/>
+            </div>
+            <aside className="page-sidebar-fixed">
+              <FavoriteFilterContainer/>
+
+              <p className="note text-center">Filters are not available.</p>
+            </aside>
+          </div>
+        </div>
+    );
+  }
+}
diff --git a/server/sonar-web/src/main/js/apps/projects/components/FavoriteProjectsContainer.js b/server/sonar-web/src/main/js/apps/projects/components/FavoriteProjectsContainer.js
new file mode 100644 (file)
index 0000000..d78dafb
--- /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 FavoriteProjects from './FavoriteProjects';
+import { fetchFavoriteProjects } from '../store/actions';
+import { getCurrentUser } from '../../../app/store/rootReducer';
+
+const mapStateToProps = state => ({
+  user: getCurrentUser(state)
+});
+
+export default connect(
+    mapStateToProps,
+    { fetchFavoriteProjects }
+)(FavoriteProjects);
index b96e7d3651b83988a0e0daea7136cc6df9cd448a..cc1ecd10506775d07631fa20a5beb0d514f32fb1 100644 (file)
  * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
  */
 import React from 'react';
-import { Route } from 'react-router';
+import { Route, IndexRoute } from 'react-router';
 import AppContainer from './components/AppContainer';
+import FavoriteProjectsContainer from './components/FavoriteProjectsContainer';
 
 export default (
-    <Route path="projects" component={AppContainer}/>
+    <Route path="projects">
+      <IndexRoute component={AppContainer}/>
+      <Route path="favorite" component={FavoriteProjectsContainer}/>
+    </Route>
 );
index 93dbc409a4d60d42951970fea75b8ce1aa8f9672..e811bd6327903ff89e1842201b832d9e26f08ea9 100644 (file)
@@ -28,6 +28,8 @@ import { getProjectsAppState } from '../../../app/store/rootReducer';
 import { getMeasuresForComponents } from '../../../api/measures';
 import { receiveComponentsMeasures } from '../../../app/store/measures/actions';
 import { convertToFilter } from './utils';
+import { getFavorites } from '../../../api/favorites';
+import { receiveFavorites } from '../../../app/store/favorites/actions';
 
 const PAGE_SIZE = 50;
 
@@ -124,3 +126,23 @@ export const fetchMoreProjects = query => (dispatch, getState) => {
   }
   return searchProjects(data).then(onReceiveMoreProjects(dispatch), onFail(dispatch));
 };
+
+export const fetchFavoriteProjects = () => dispatch => {
+  dispatch(updateState({ loading: true }));
+
+  return getFavorites().then(favorites => {
+    dispatch(receiveFavorites(favorites));
+
+    const projects = favorites.filter(component => component.qualifier === 'TRK');
+
+    dispatch(receiveComponents(projects));
+    dispatch(receiveProjects(projects, []));
+    dispatch(fetchProjectMeasures(projects)).then(() => {
+      dispatch(updateState({ loading: false }));
+    });
+    dispatch(updateState({
+      total: projects.length,
+      pageIndex: 1,
+    }));
+  }, onFail(dispatch));
+};
index e1a4cc1245b4884afc67b715115135b0645822d8..cc1105ba0aa4f2c395ef23fb61d1a4f247c83e39 100644 (file)
 .projects-facets-reset .button {
 
 }
+
+.projects-favorite-filter {
+  width: 100%;
+  margin-bottom: 30px;
+  padding-left: 10px;
+  padding-right: 10px;
+  box-sizing: border-box;
+}
+
+.projects-favorite-filter > a {
+  width: 50%;
+}
index 5945dd593b9ddef12d0082f596061eb9eae9ad12..4de11a1c3e873a5b96baac60eabea4ae56ce8efe 100644 (file)
@@ -107,7 +107,7 @@ input[type=button] {
   outline: none;
   transition: border-color 0.2s ease;
 
-  &:hover, &:focus {
+  &:hover, &:focus, &.button-active {
     background: @darkBlue;
     color: #fff;
   }
index 1174555457f29a528d3552689595a3bfd21069f1..b1d32b5cc034f79eb6a3977f26146d24ac689b18 100644 (file)
@@ -23,4 +23,8 @@ class ProjectsController < ApplicationController
 
   end
 
+  def favorite
+     render :action => 'index'
+  end
+
 end