aboutsummaryrefslogtreecommitdiffstats
path: root/server/sonar-web/src/main/js/apps/account
diff options
context:
space:
mode:
authorStas Vilchik <vilchiks@gmail.com>2016-01-26 14:34:51 +0100
committerStas Vilchik <vilchiks@gmail.com>2016-01-28 16:14:24 +0100
commitde0b07fffd01cc2552c9a4a50ebbc994fb684400 (patch)
treec66be7f9c8ae4761ef7b4cc64f96f7e51cd954b4 /server/sonar-web/src/main/js/apps/account
parent2a981676b2f034933c01b7fd7b745a61263690ac (diff)
downloadsonarqube-de0b07fffd01cc2552c9a4a50ebbc994fb684400.tar.gz
sonarqube-de0b07fffd01cc2552c9a4a50ebbc994fb684400.zip
SONAR-7230 Move user notifications into separate page of "My Account" space
Diffstat (limited to 'server/sonar-web/src/main/js/apps/account')
-rw-r--r--server/sonar-web/src/main/js/apps/account/app.js21
-rw-r--r--server/sonar-web/src/main/js/apps/account/components/GlobalNotifications.js53
-rw-r--r--server/sonar-web/src/main/js/apps/account/components/Nav.js5
-rw-r--r--server/sonar-web/src/main/js/apps/account/components/Notifications.js41
-rw-r--r--server/sonar-web/src/main/js/apps/account/components/NotificationsList.js42
-rw-r--r--server/sonar-web/src/main/js/apps/account/components/ProjectNotifications.js102
-rw-r--r--server/sonar-web/src/main/js/apps/account/containers/NotificationsContainer.js42
-rw-r--r--server/sonar-web/src/main/js/apps/account/store/actions.js35
-rw-r--r--server/sonar-web/src/main/js/apps/account/store/configureStore.js36
-rw-r--r--server/sonar-web/src/main/js/apps/account/store/reducers.js69
10 files changed, 435 insertions, 11 deletions
diff --git a/server/sonar-web/src/main/js/apps/account/app.js b/server/sonar-web/src/main/js/apps/account/app.js
index 5af38f5b6c3..4a78a6a04fb 100644
--- a/server/sonar-web/src/main/js/apps/account/app.js
+++ b/server/sonar-web/src/main/js/apps/account/app.js
@@ -21,10 +21,12 @@ import React from 'react';
import { render } from 'react-dom';
import { Router, Route, IndexRoute, Redirect } from 'react-router';
import { createHistory, useBasename } from 'history';
+import { Provider } from 'react-redux';
+import configureStore from './store/configureStore';
import AccountApp from './containers/AccountApp';
import Home from './components/Home';
-import Notifications from './components/Notifications';
+import NotificationsContainer from './containers/NotificationsContainer';
window.sonarqube.appStarted.then(options => {
const el = document.querySelector(options.el);
@@ -33,16 +35,21 @@ window.sonarqube.appStarted.then(options => {
basename: window.baseUrl + '/account'
});
+ const store = configureStore();
+
document.querySelector('html').classList.add('dashboard-page');
document.querySelector('#container').classList.add('page-wrapper-context');
render((
- <Router history={history}>
- <Route path="/" component={AccountApp}>
- <IndexRoute component={Home}/>
+ <Provider store={store}>
+ <Router history={history}>
+ <Route path="/" component={AccountApp}>
+ <IndexRoute component={Home}/>
+ <Route path="notifications" component={NotificationsContainer}/>
- <Redirect from="/index" to="/"/>
- </Route>
- </Router>
+ <Redirect from="/index" to="/"/>
+ </Route>
+ </Router>
+ </Provider>
), el);
});
diff --git a/server/sonar-web/src/main/js/apps/account/components/GlobalNotifications.js b/server/sonar-web/src/main/js/apps/account/components/GlobalNotifications.js
new file mode 100644
index 00000000000..5f5de5de9b4
--- /dev/null
+++ b/server/sonar-web/src/main/js/apps/account/components/GlobalNotifications.js
@@ -0,0 +1,53 @@
+/*
+ * 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 NotificationsList from './NotificationsList';
+import { translate } from '../../../helpers/l10n';
+
+export default function GlobalNotifications ({ notifications, channels }) {
+ return (
+ <div>
+ <header className="page-header">
+ <h2 className="page-title">
+ {translate('my_profile.overall_notifications.title')}
+ </h2>
+ </header>
+
+ <table className="form">
+ <thead>
+ <tr>
+ <th></th>
+ {channels.map(channel => (
+ <th key={channel} className="text-center">
+ <h4>{translate('notification.channel', channel)}</h4>
+ </th>
+ ))}
+ </tr>
+ </thead>
+
+ <NotificationsList
+ notifications={notifications}
+ checkboxId={(d, c) => `global_notifs_${d}_${c}`}
+ checkboxName={(d, c) => `global_notifs[${d}.${c}]`}/>
+ </table>
+ </div>
+ );
+}
diff --git a/server/sonar-web/src/main/js/apps/account/components/Nav.js b/server/sonar-web/src/main/js/apps/account/components/Nav.js
index 3a85c7da307..e19213d3062 100644
--- a/server/sonar-web/src/main/js/apps/account/components/Nav.js
+++ b/server/sonar-web/src/main/js/apps/account/components/Nav.js
@@ -38,6 +38,11 @@ const Nav = () => (
<i className="icon-home"/>
</IndexLink>
</li>
+ <li>
+ <IndexLink to="notifications" activeClassName="active">
+ {translate('my_account.notifications')}
+ </IndexLink>
+ </li>
</ul>
</div>
</nav>
diff --git a/server/sonar-web/src/main/js/apps/account/components/Notifications.js b/server/sonar-web/src/main/js/apps/account/components/Notifications.js
index 195a134c15c..45a9d5839eb 100644
--- a/server/sonar-web/src/main/js/apps/account/components/Notifications.js
+++ b/server/sonar-web/src/main/js/apps/account/components/Notifications.js
@@ -19,8 +19,41 @@
*/
import React from 'react';
-const Notifications = () => (
- <h1>Notifications</h1>
-);
+import GlobalNotifications from './GlobalNotifications';
+import ProjectNotifications from './ProjectNotifications';
+import { translate } from '../../../helpers/l10n';
-export default Notifications;
+export default function Notifications ({ globalNotifications, projectNotifications, onAddProject, onRemoveProject }) {
+ const channels = globalNotifications[0].channels.map(c => c.id);
+
+ return (
+ <div>
+ <p className="big-spacer-bottom">
+ {translate('notification.dispatcher.information')}
+ </p>
+ <form id="notif_form" method="post" action={`${window.baseUrl}/account/update_notifications`}>
+ <div className="columns">
+ <div className="column-half">
+ <GlobalNotifications
+ notifications={globalNotifications}
+ channels={channels}/>
+ </div>
+
+ <div className="column-half">
+ <ProjectNotifications
+ notifications={projectNotifications}
+ channels={channels}
+ onAddProject={onAddProject}
+ onRemoveProject={onRemoveProject}/>
+ </div>
+ </div>
+
+ <p className="big-spacer-top panel panel-vertical bordered-top text-right">
+ <button id="submit-notifications" type="submit">
+ {translate('my_profile.notifications.submit')}
+ </button>
+ </p>
+ </form>
+ </div>
+ );
+}
diff --git a/server/sonar-web/src/main/js/apps/account/components/NotificationsList.js b/server/sonar-web/src/main/js/apps/account/components/NotificationsList.js
new file mode 100644
index 00000000000..a8044b4b142
--- /dev/null
+++ b/server/sonar-web/src/main/js/apps/account/components/NotificationsList.js
@@ -0,0 +1,42 @@
+/*
+ * 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 { translate } from '../../../helpers/l10n';
+
+export default function NotificationsList ({ notifications, checkboxName, checkboxId }) {
+ return (
+ <tbody>
+ {notifications.map(notification => (
+ <tr key={notification.dispatcher}>
+ <td>{translate('notification.dispatcher', notification.dispatcher)}</td>
+ {notification.channels.map(channel => (
+ <td key={channel.id} className="text-center">
+ <input defaultChecked={channel.checked}
+ id={checkboxId(notification.dispatcher, channel.id)}
+ name={checkboxName(notification.dispatcher, channel.id)}
+ type="checkbox"/>
+ </td>
+ ))}
+ </tr>
+ ))}
+ </tbody>
+ );
+}
diff --git a/server/sonar-web/src/main/js/apps/account/components/ProjectNotifications.js b/server/sonar-web/src/main/js/apps/account/components/ProjectNotifications.js
new file mode 100644
index 00000000000..5683e50e1d8
--- /dev/null
+++ b/server/sonar-web/src/main/js/apps/account/components/ProjectNotifications.js
@@ -0,0 +1,102 @@
+/*
+ * 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 Select from 'react-select';
+
+import NotificationsList from './NotificationsList';
+import { translate } from '../../../helpers/l10n';
+import { getProjectsWithInternalId } from '../../../api/components';
+
+export default function ProjectNotifications ({ notifications, channels, onAddProject, onRemoveProject }) {
+ const loadOptions = (query) => {
+ return getProjectsWithInternalId(query)
+ .then(results => results.map(r => {
+ return {
+ value: r.id,
+ label: r.text
+ };
+ }))
+ .then(options => {
+ return { options };
+ });
+ };
+
+ const handleAddProject = (selected) => {
+ const project = {
+ internalId: selected.value,
+ name: selected.label
+ };
+ onAddProject(project);
+ };
+
+ const handleRemoveProject = (project) => (
+ (e) => {
+ e.preventDefault;
+ onRemoveProject(project);
+ }
+ );
+
+ return (
+ <div>
+ <header className="page-header">
+ <h2 className="page-title">
+ {translate('my_profile.per_project_notifications.title')}
+ </h2>
+ <div className="pull-right">
+ <Select.Async
+ name="new_project"
+ style={{ width: '150px' }}
+ loadOptions={loadOptions}
+ onChange={handleAddProject}
+ placeholder="Add Project"/>
+ </div>
+ </header>
+
+ {!notifications.length && (
+ <div className="note">
+ {translate('my_account.no_project_notifications')}
+ </div>
+ )}
+
+ {notifications.map(p => (
+ <table key={p.project.internalId} className="form spacer-bottom">
+ <thead>
+ <tr>
+ <th>
+ <a onClick={handleRemoveProject(p.project)}
+ className="spacer-right icon-delete js-delete-project" href="#"></a>
+ <h3 className="display-inline-block">{p.project.name}</h3>
+ </th>
+ {channels.map(channel => (
+ <th key={channel} className="text-center">
+ <h4>{translate('notification.channel', channel)}</h4>
+ </th>
+ ))}
+ </tr>
+ </thead>
+ <NotificationsList
+ notifications={p.notifications}
+ checkboxId={(d, c) => `project_notifs_${p.project.internalId}_${d}_${c}`}
+ checkboxName={(d, c) => `project_notifs[${p.project.internalId}][${d}][${c}]`}/>
+ </table>
+ ))}
+ </div>
+ );
+}
diff --git a/server/sonar-web/src/main/js/apps/account/containers/NotificationsContainer.js b/server/sonar-web/src/main/js/apps/account/containers/NotificationsContainer.js
new file mode 100644
index 00000000000..84328295905
--- /dev/null
+++ b/server/sonar-web/src/main/js/apps/account/containers/NotificationsContainer.js
@@ -0,0 +1,42 @@
+/*
+ * 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);
diff --git a/server/sonar-web/src/main/js/apps/account/store/actions.js b/server/sonar-web/src/main/js/apps/account/store/actions.js
new file mode 100644
index 00000000000..bee94192a5c
--- /dev/null
+++ b/server/sonar-web/src/main/js/apps/account/store/actions.js
@@ -0,0 +1,35 @@
+/*
+ * 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 ADD_PROJECT_NOTIFICATIONS = 'ADD_PROJECT_NOTIFICATIONS';
+export const REMOVE_PROJECT_NOTIFICATIONS = 'REMOVE_PROJECT_NOTIFICATIONS';
+
+export function addProjectNotifications (project) {
+ return {
+ type: ADD_PROJECT_NOTIFICATIONS,
+ project
+ };
+}
+
+export function removeProjectNotifications (project) {
+ return {
+ type: REMOVE_PROJECT_NOTIFICATIONS,
+ project
+ };
+}
diff --git a/server/sonar-web/src/main/js/apps/account/store/configureStore.js b/server/sonar-web/src/main/js/apps/account/store/configureStore.js
new file mode 100644
index 00000000000..e412913dc76
--- /dev/null
+++ b/server/sonar-web/src/main/js/apps/account/store/configureStore.js
@@ -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 { 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);
+}
diff --git a/server/sonar-web/src/main/js/apps/account/store/reducers.js b/server/sonar-web/src/main/js/apps/account/store/reducers.js
new file mode 100644
index 00000000000..23921840d8b
--- /dev/null
+++ b/server/sonar-web/src/main/js/apps/account/store/reducers.js
@@ -0,0 +1,69 @@
+/*
+ * 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 { ADD_PROJECT_NOTIFICATIONS, REMOVE_PROJECT_NOTIFICATIONS } from './actions';
+
+function addProjectNotifications (state, project) {
+ const found = state.find(notification => {
+ return 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 => {
+ return notification.project.internalId !== project.internalId;
+ });
+}
+
+export const initialState = {
+ projectNotifications: window.sonarqube.notifications.project
+};
+
+export default function (state = initialState, action) {
+ switch (action.type) {
+ 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;
+ }
+}