--- /dev/null
+.account-header {
+ position: fixed;
+ top: 30px;
+ left: 0;
+ right: 0;
+ z-index: 420;
+ background-color: #f3f3f3;
+}
+
+.account-nav {
+
+}
+
+.account-nav .nav-tabs {
+ width: 100%;
+}
+
+.account-user {
+ padding: 10px;
+}
+
+.account-nav-avatar {
+ float: left;
+ margin-right: 20px;
+}
+
+.account-page {
+ padding-top: 102px;
+}
+
+.account-bar-chart .bar-chart-bar {
+ fill: #4b9fd5;
+}
+
+.account-bar-chart .bar-chart-tick {
+ fill: #777;
+ font-size: 12px;
+ text-anchor: middle;
+}
+
+.account-bar-chart .histogram-tick {
+ text-anchor: end;
+}
+
+.account-bar-chart .histogram-value {
+ text-anchor: start;
+}
import { render } from 'react-dom';
import { Router, Route, IndexRoute, Redirect, useRouterHistory } from 'react-router';
import { createHistory } from 'history';
-import { Provider } from 'react-redux';
-
-import configureStore from './store/configureStore';
-import AccountApp from './containers/AccountApp';
+import AccountApp from './components/AccountApp';
import Home from './components/Home';
-import NotificationsContainer from './containers/NotificationsContainer';
-import SecurityContainer from './containers/SecurityContainer';
-import IssuesContainer from './containers/IssuesContainer';
-
-import './styles/account.css';
+import NotificationsContainer from './components/NotificationsContainer';
+import Security from './components/Security';
+import Issues from './components/Issues';
window.sonarqube.appStarted.then(options => {
const el = document.querySelector(options.el);
basename: window.baseUrl + '/account'
});
- const store = configureStore();
-
render((
- <Provider store={store}>
- <Router history={history}>
- <Route path="/" component={AccountApp}>
- <IndexRoute component={Home}/>
- <Route path="issues" component={IssuesContainer}/>
- <Route path="notifications" component={NotificationsContainer}/>
- <Route path="security" component={SecurityContainer}/>
+ <Router history={history}>
+ <Route path="/" component={AccountApp}>
+ <IndexRoute component={Home}/>
+ <Route path="issues" component={Issues}/>
+ <Route path="notifications" component={NotificationsContainer}/>
+ <Route path="security" component={Security}/>
- <Redirect from="/index" to="/"/>
- </Route>
- </Router>
- </Provider>
+ <Redirect from="/index" to="/"/>
+ </Route>
+ </Router>
), el);
});
--- /dev/null
+/*
+ * SonarQube
+ * Copyright (C) 2009-2016 SonarSource SA
+ * mailto:contact AT sonarsource DOT com
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+import React, { Component, cloneElement } from 'react';
+import Nav from './Nav';
+import { getCurrentUser } from '../../../api/users';
+import { getIssueFilters } from '../../../api/issues';
+import '../account.css';
+
+export default class AccountApp extends Component {
+ state = {
+ loading: true
+ };
+
+ componentDidMount () {
+ this.mounted = true;
+ Promise.all([
+ this.loadUser(),
+ this.loadFavoriteIssueFilters()
+ ]).then(() => this.finishLoading());
+ }
+
+ componentWillUnmount () {
+ this.mounted = false;
+ }
+
+ loadUser () {
+ return getCurrentUser().then(user => {
+ if (this.mounted) {
+ this.setState({ user });
+ }
+ });
+ }
+
+ loadFavoriteIssueFilters () {
+ return getIssueFilters().then(issueFilters => {
+ const favoriteIssueFilters = issueFilters.filter(f => f.favorite);
+ this.setState({ issueFilters: favoriteIssueFilters });
+ });
+ }
+
+ finishLoading () {
+ if (this.mounted) {
+ this.setState({ loading: false });
+ }
+ }
+
+ render () {
+ const { user, issueFilters, loading } = this.state;
+
+ if (loading) {
+ return (
+ <div>
+ <i className="spinner spinner-margin"/>
+ </div>
+ );
+ }
+
+ const { favorites } = window.sonarqube.user;
+ const measureFilters = window.sonarqube.user.favoriteMeasureFilters;
+ const children = cloneElement(this.props.children, {
+ measureFilters,
+ user,
+ favorites,
+ issueFilters
+ });
+
+ return (
+ <div className="account-page">
+ <Nav user={user}/>
+ {children}
+ </div>
+ );
+ }
+}
--- /dev/null
+/*
+ * SonarQube
+ * Copyright (C) 2009-2016 SonarSource SA
+ * mailto:contact AT sonarsource DOT com
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+import React, { Component } from 'react';
+
+import IssuesApp from '../issues-app';
+
+export default class Issues extends Component {
+ componentDidMount () {
+ this.issuesApp = IssuesApp;
+ this.issuesApp.start({
+ el: this.refs.container
+ });
+ }
+
+ componentWillUnmount () {
+ this.issuesApp.stop();
+ }
+
+ render () {
+ return <div ref="container"></div>;
+ }
+}
import ProjectNotifications from './ProjectNotifications';
import { translate } from '../../../helpers/l10n';
-export default function Notifications ({ globalNotifications, projectNotifications, onAddProject, onRemoveProject }) {
+const Notifications = ({ globalNotifications, projectNotifications, onAddProject, onRemoveProject }) => {
const channels = globalNotifications[0].channels.map(c => c.id);
return (
</form>
</div>
);
-}
+};
+
+export default Notifications;
--- /dev/null
+/*
+ * 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 Notifications from './Notifications';
+
+export default class NotificationsContainer extends React.Component {
+ state = {
+ globalNotifications: window.sonarqube.notifications.global,
+ projectNotifications: window.sonarqube.notifications.project
+ };
+
+ componentWillMount () {
+ this.handleAddProject = this.handleAddProject.bind(this);
+ this.handleRemoveProject = this.handleRemoveProject.bind(this);
+ }
+
+ handleAddProject (project) {
+ const { projectNotifications } = this.state;
+ const found = projectNotifications
+ .find(notification => notification.project.internalId === project.internalId);
+
+ if (!found) {
+ const newProjectNotification = {
+ project,
+ notifications: window.sonarqube.notifications.projectDispatchers.map(dispatcher => {
+ const channels = window.sonarqube.notifications.channels.map(channel => {
+ return { id: channel, checked: false };
+ });
+ return { dispatcher, channels };
+ })
+ };
+
+ this.setState({
+ projectNotifications: [...projectNotifications, newProjectNotification]
+ });
+ }
+ }
+
+ handleRemoveProject (project) {
+ const projectNotifications = this.state.projectNotifications
+ .filter(notification => notification.project.internalId !== project.internalId);
+ this.setState({ projectNotifications });
+ }
+
+ render () {
+ return (
+ <Notifications
+ globalNotifications={this.state.globalNotifications}
+ projectNotifications={this.state.projectNotifications}
+ onAddProject={this.handleAddProject}
+ onRemoveProject={this.handleRemoveProject}/>
+ );
+ }
+}
import Password from './Password';
import Tokens from './Tokens';
-export default function Security ({ user }) {
+const Security = ({ user }) => {
return (
<div className="page page-limited">
<div className="columns">
</div>
</div>
);
-}
+};
+
+export default Security;
import UserExternalIdentity from './UserExternalIdentity';
import Avatar from '../../../components/shared/avatar';
-export default function UserCard ({ user }) {
+const UserCard = ({ user }) => {
return (
<section className="account-user clearfix">
<div id="avatar" className="account-nav-avatar">
<div id="email" className="little-spacer-top">{user.email}</div>
</section>
);
-}
+};
+
+export default UserCard;
+++ /dev/null
-/*
- * SonarQube
- * Copyright (C) 2009-2016 SonarSource SA
- * mailto:contact AT sonarsource DOT com
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation; either
- * version 3 of the License, or (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public License
- * along with this program; if not, write to the Free Software Foundation,
- * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
- */
-import React, { Component, cloneElement } from 'react';
-import { connect } from 'react-redux';
-
-import Nav from '../components/Nav';
-import { fetchUser } from '../store/actions';
-import { getIssueFilters } from '../../../api/issues';
-
-class AccountApp extends Component {
- state = {};
-
- componentDidMount () {
- this.props.fetchUser();
- this.fetchFavoriteIssueFilters();
- }
-
- fetchFavoriteIssueFilters () {
- getIssueFilters().then(issueFilters => {
- const favoriteIssueFilters = issueFilters.filter(f => f.favorite);
- this.setState({ issueFilters: favoriteIssueFilters });
- });
- }
-
- render () {
- const { user } = this.props;
-
- if (!user) {
- return null;
- }
-
- const { favorites } = window.sonarqube.user;
- const measureFilters = window.sonarqube.user.favoriteMeasureFilters;
- const { issueFilters } = this.state;
- const children = cloneElement(this.props.children, {
- measureFilters,
- user,
- favorites,
- issueFilters
- });
-
- return (
- <div className="account-page">
- <Nav user={user}/>
- {children}
- </div>
- );
- }
-}
-
-function mapStateToProps (state) {
- return { user: state.user };
-}
-
-function mapDispatchToProps (dispatch) {
- return { fetchUser: () => dispatch(fetchUser()) };
-}
-
-export default connect(
- mapStateToProps,
- mapDispatchToProps
-)(AccountApp);
+++ /dev/null
-/*
- * SonarQube
- * Copyright (C) 2009-2016 SonarSource SA
- * mailto:contact AT sonarsource DOT com
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation; either
- * version 3 of the License, or (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public License
- * along with this program; if not, write to the Free Software Foundation,
- * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
- */
-import React, { Component } from 'react';
-
-import IssuesApp from '../issues-app';
-
-export default class IssuesContainer extends Component {
- componentDidMount () {
- this.issuesApp = IssuesApp;
- this.issuesApp.start({
- el: this.refs.container
- });
- }
-
- componentWillUnmount () {
- this.issuesApp.stop();
- }
-
- render () {
- return <div ref="container"></div>;
- }
-}
+++ /dev/null
-/*
- * 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 { addProjectNotifications, removeProjectNotifications } from '../store/actions';
-import Notifications from './../components/Notifications';
-
-function mapStateToProps (state) {
- return {
- globalNotifications: window.sonarqube.notifications.global,
- projectNotifications: state.projectNotifications
- };
-}
-
-function mapDispatchToProps (dispatch) {
- return {
- onAddProject: project => dispatch(addProjectNotifications(project)),
- onRemoveProject: project => dispatch(removeProjectNotifications(project))
- };
-}
-
-export default connect(
- mapStateToProps,
- mapDispatchToProps
-)(Notifications);
+++ /dev/null
-/*
- * 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 Security from './../components/Security';
-
-function mapStateToProps (state) {
- return { user: state.user };
-}
-
-export default connect(mapStateToProps)(Security);
+++ /dev/null
-/*
- * 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 { getCurrentUser } from '../../../api/users';
-
-export const REQUEST_USER = 'REQUEST_USER';
-export const RECEIVE_USER = 'RECEIVE_USER';
-export const ADD_PROJECT_NOTIFICATIONS = 'ADD_PROJECT_NOTIFICATIONS';
-export const REMOVE_PROJECT_NOTIFICATIONS = 'REMOVE_PROJECT_NOTIFICATIONS';
-
-export function requestUser () {
- return {
- type: REQUEST_USER
- };
-}
-
-export function receiveUser (user) {
- return {
- type: RECEIVE_USER,
- user
- };
-}
-
-export function addProjectNotifications (project) {
- return {
- type: ADD_PROJECT_NOTIFICATIONS,
- project
- };
-}
-
-export function removeProjectNotifications (project) {
- return {
- type: REMOVE_PROJECT_NOTIFICATIONS,
- project
- };
-}
-
-export function fetchUser () {
- return dispatch => {
- dispatch(requestUser());
- return getCurrentUser().then(user => dispatch(receiveUser(user)));
- };
-}
+++ /dev/null
-/*
- * 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 { createStore, applyMiddleware } from 'redux';
-import thunk from 'redux-thunk';
-import createLogger from 'redux-logger';
-import rootReducer from './reducers';
-
-const logger = createLogger({
- predicate: () => process.env.NODE_ENV !== 'production'
-});
-
-const createStoreWithMiddleware = applyMiddleware(
- thunk,
- logger
-)(createStore);
-
-export default function configureStore () {
- return createStoreWithMiddleware(rootReducer);
-}
+++ /dev/null
-/*
- * 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_USER, ADD_PROJECT_NOTIFICATIONS, REMOVE_PROJECT_NOTIFICATIONS } from './actions';
-
-function addProjectNotifications (state, project) {
- const found = state.find(notification => notification.project.internalId === project.internalId);
-
- if (found) {
- return state;
- }
-
- const newProjectNotification = {
- project,
- notifications: window.sonarqube.notifications.projectDispatchers.map(dispatcher => {
- const channels = window.sonarqube.notifications.channels.map(channel => {
- return { id: channel, checked: false };
- });
- return { dispatcher, channels };
- })
- };
-
- return [...state, newProjectNotification];
-}
-
-function removeProjectNotifications (state, project) {
- return state.filter(notification => notification.project.internalId !== project.internalId);
-}
-
-export const initialState = {
- user: null,
- projectNotifications: window.sonarqube.notifications.project
-};
-
-export default function (state = initialState, action = {}) {
- switch (action.type) {
- case RECEIVE_USER:
- return {
- ...state,
- user: action.user
- };
- case ADD_PROJECT_NOTIFICATIONS:
- return {
- ...state,
- projectNotifications: addProjectNotifications(state.projectNotifications, action.project)
- };
- case REMOVE_PROJECT_NOTIFICATIONS:
- return {
- ...state,
- projectNotifications: removeProjectNotifications(state.projectNotifications, action.project)
- };
- default:
- return state;
- }
-}
+++ /dev/null
-.account-header {
- position: fixed;
- top: 30px;
- left: 0;
- right: 0;
- z-index: 420;
- background-color: #f3f3f3;
-}
-
-.account-nav {
-
-}
-
-.account-nav .nav-tabs {
- width: 100%;
-}
-
-.account-user {
- padding: 10px;
-}
-
-.account-nav-avatar {
- float: left;
- margin-right: 20px;
-}
-
-.account-page {
- padding-top: 102px;
-}
-
-.account-bar-chart .bar-chart-bar {
- fill: #4b9fd5;
-}
-
-.account-bar-chart .bar-chart-tick {
- fill: #777;
- font-size: 12px;
- text-anchor: middle;
-}
-
-.account-bar-chart .histogram-tick {
- text-anchor: end;
-}
-
-.account-bar-chart .histogram-value {
- text-anchor: start;
-}