From 5bcae889e71152a593781551b535d45eaa959c8e Mon Sep 17 00:00:00 2001 From: Stas Vilchik Date: Fri, 12 Aug 2016 10:18:02 +0200 Subject: [PATCH] SONAR-7919 handle errors and display success messages --- .../sonar-web/src/main/js/apps/code/utils.js | 5 ++ .../js/apps/project-admin/key/BulkUpdate.js | 56 +++++++++++-------- .../project-admin/key/FineGrainedUpdate.js | 1 - .../src/main/js/apps/project-admin/key/Key.js | 45 ++++++++++++--- .../js/apps/project-admin/key/UpdateForm.js | 10 ++++ .../main/js/apps/project-admin/key/utils.js | 25 +++++++++ .../key/views/UpdateKeyConfirmation.js | 9 +-- .../js/components/store/globalMessages.js | 11 ++++ 8 files changed, 122 insertions(+), 40 deletions(-) create mode 100644 server/sonar-web/src/main/js/apps/project-admin/key/utils.js diff --git a/server/sonar-web/src/main/js/apps/code/utils.js b/server/sonar-web/src/main/js/apps/code/utils.js index aaa5974a794..169f7175ac9 100644 --- a/server/sonar-web/src/main/js/apps/code/utils.js +++ b/server/sonar-web/src/main/js/apps/code/utils.js @@ -163,6 +163,11 @@ export function loadMoreChildren (componentKey, page) { }); } +/** + * Parse response of failed request + * @param {Error} error + * @returns {Promise} + */ export function parseError (error) { const DEFAULT_MESSAGE = translate('default_error_message'); diff --git a/server/sonar-web/src/main/js/apps/project-admin/key/BulkUpdate.js b/server/sonar-web/src/main/js/apps/project-admin/key/BulkUpdate.js index 1624faddc3f..10524a81dcd 100644 --- a/server/sonar-web/src/main/js/apps/project-admin/key/BulkUpdate.js +++ b/server/sonar-web/src/main/js/apps/project-admin/key/BulkUpdate.js @@ -18,15 +18,25 @@ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ import React from 'react'; +import { connect } from 'react-redux'; import BulkUpdateForm from './BulkUpdateForm'; import BulkUpdateResults from './BulkUpdateResults'; import { translate, translateWithParameters } from '../../../helpers/l10n'; import { bulkChangeKey } from '../../../api/components'; -import { getComponentUrl } from '../../../helpers/urls'; +import { parseError } from '../../code/utils'; +import { + addGlobalErrorMessage, + addGlobalSuccessMessage, + closeAllGlobalMessages +} from '../../../components/store/globalMessages'; +import { reloadUpdateKeyPage } from './utils'; -export default class BulkUpdate extends React.Component { +class BulkUpdate extends React.Component { static propTypes = { - component: React.PropTypes.object.isRequired + component: React.PropTypes.object.isRequired, + addGlobalErrorMessage: React.PropTypes.func.isRequired, + addGlobalSuccessMessage: React.PropTypes.func.isRequired, + closeAllGlobalMessages: React.PropTypes.func.isRequired }; state = { @@ -41,17 +51,20 @@ export default class BulkUpdate extends React.Component { handleConfirm () { this.setState({ updating: true }); - + const { component } = this.props; const { replace, by } = this.state; + bulkChangeKey(component.key, replace, by).then(r => { const result = r.keys.find(result => result.key === component.key); const newComponentKey = result != null ? result.newKey : component.key; - this.setState({ - updating: false, - updated: true, - newComponentKey - }); + + this.props.addGlobalSuccessMessage(translate('update_key.key_updated')); + this.setState({ updating: false }); + reloadUpdateKeyPage(newComponentKey); + }).catch(e => { + this.setState({ updating: false }); + parseError(e).then(message => this.props.addGlobalErrorMessage(message)); }); } @@ -59,6 +72,9 @@ export default class BulkUpdate extends React.Component { const { component } = this.props; bulkChangeKey(component.key, replace, by, true).then(r => { this.setState({ results: r.keys, replace, by }); + this.props.closeAllGlobalMessages(); + }).catch(e => { + parseError(e).then(message => this.props.addGlobalErrorMessage(message)); }); } @@ -70,20 +86,6 @@ export default class BulkUpdate extends React.Component { ); } - renderUpdated () { - return ( -
-
- {translate('update_key.key_updated')} - {' '} - - {translate('check_project')} - -
-
- ); - } - render () { const { component } = this.props; const { updating, updated } = this.state; @@ -125,3 +127,11 @@ export default class BulkUpdate extends React.Component { ); } } + +export default connect( + null, { + addGlobalErrorMessage, + addGlobalSuccessMessage, + closeAllGlobalMessages + } +)(BulkUpdate); diff --git a/server/sonar-web/src/main/js/apps/project-admin/key/FineGrainedUpdate.js b/server/sonar-web/src/main/js/apps/project-admin/key/FineGrainedUpdate.js index 6f7deacf0ed..e86404f8c16 100644 --- a/server/sonar-web/src/main/js/apps/project-admin/key/FineGrainedUpdate.js +++ b/server/sonar-web/src/main/js/apps/project-admin/key/FineGrainedUpdate.js @@ -20,7 +20,6 @@ import React from 'react'; import UpdateKeyForm from './UpdateKeyForm'; import QualifierIcon from '../../../components/shared/qualifier-icon'; -import { translate } from '../../../helpers/l10n'; export default class FineGrainedUpdate extends React.Component { render () { diff --git a/server/sonar-web/src/main/js/apps/project-admin/key/Key.js b/server/sonar-web/src/main/js/apps/project-admin/key/Key.js index 47b61bc3e6e..b7c7b307ef4 100644 --- a/server/sonar-web/src/main/js/apps/project-admin/key/Key.js +++ b/server/sonar-web/src/main/js/apps/project-admin/key/Key.js @@ -24,15 +24,26 @@ import Header from './Header'; import UpdateForm from './UpdateForm'; import BulkUpdate from './BulkUpdate'; import FineGrainedUpdate from './FineGrainedUpdate'; +import GlobalMessagesContainer from '../components/GlobalMessagesContainer'; import { getProjectModules } from '../store/rootReducer'; import { fetchProjectModules, changeKey } from '../store/actions'; import { translate } from '../../../helpers/l10n'; +import { + addGlobalErrorMessage, + closeAllGlobalMessages, + addGlobalSuccessMessage +} from '../../../components/store/globalMessages'; +import { parseError } from '../../code/utils'; +import { reloadUpdateKeyPage } from './utils'; class Key extends React.Component { static propTypes = { component: React.PropTypes.object.isRequired, fetchProjectModules: React.PropTypes.func.isRequired, - changeKey: React.PropTypes.func.isRequired + changeKey: React.PropTypes.func.isRequired, + addGlobalErrorMessage: React.PropTypes.func.isRequired, + addGlobalSuccessMessage: React.PropTypes.func.isRequired, + closeAllGlobalMessages: React.PropTypes.func.isRequired }; state = { @@ -49,10 +60,13 @@ class Key extends React.Component { handleChangeKey (key, newKey) { return this.props.changeKey(key, newKey).then(() => { + this.props.addGlobalSuccessMessage(translate('update_key.key_updated')); + if (key === this.props.component.key) { - window.location = window.baseUrl + - '/project/key?id=' + encodeURIComponent(newKey); + reloadUpdateKeyPage(newKey); } + }).catch(e => { + parseError(e).then(this.props.addGlobalErrorMessage); }); } @@ -60,6 +74,7 @@ class Key extends React.Component { e.preventDefault(); e.target.blur(); this.setState({ tab }); + this.props.closeAllGlobalMessages(); } render () { @@ -79,9 +94,12 @@ class Key extends React.Component { )} {noModules && ( - +
+ + +
)} {hasModules && ( @@ -107,6 +125,8 @@ class Key extends React.Component { + + {tab === 'bulk' && ( )} @@ -115,7 +135,9 @@ class Key extends React.Component { + onKeyChange={this.handleChangeKey.bind(this)} + onSuccess={this.props.closeAllGlobalMessages} + onError={this.props.addGlobalErrorMessage}/> )} )} @@ -129,6 +151,11 @@ const mapStateToProps = (state, ownProps) => ({ }); export default connect( - mapStateToProps, - { fetchProjectModules, changeKey } + mapStateToProps, { + fetchProjectModules, + changeKey, + addGlobalErrorMessage, + addGlobalSuccessMessage, + closeAllGlobalMessages + } )(Key); diff --git a/server/sonar-web/src/main/js/apps/project-admin/key/UpdateForm.js b/server/sonar-web/src/main/js/apps/project-admin/key/UpdateForm.js index 701d2070957..1031ad1ba6c 100644 --- a/server/sonar-web/src/main/js/apps/project-admin/key/UpdateForm.js +++ b/server/sonar-web/src/main/js/apps/project-admin/key/UpdateForm.js @@ -46,6 +46,11 @@ export default class UpdateForm extends React.Component { this.setState({ newKey }); } + handleReset (e) { + e.preventDefault(); + this.setState({ newKey: null }); + } + render () { const value = this.state.newKey != null ? this.state.newKey : @@ -69,6 +74,11 @@ export default class UpdateForm extends React.Component { + {' '} + ); diff --git a/server/sonar-web/src/main/js/apps/project-admin/key/utils.js b/server/sonar-web/src/main/js/apps/project-admin/key/utils.js new file mode 100644 index 00000000000..a17481bed35 --- /dev/null +++ b/server/sonar-web/src/main/js/apps/project-admin/key/utils.js @@ -0,0 +1,25 @@ +/* + * 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 reloadUpdateKeyPage = componentKey => { + setTimeout(() => { + window.location = window.baseUrl + + '/project/key?id=' + encodeURIComponent(componentKey); + }, 1000); +}; diff --git a/server/sonar-web/src/main/js/apps/project-admin/key/views/UpdateKeyConfirmation.js b/server/sonar-web/src/main/js/apps/project-admin/key/views/UpdateKeyConfirmation.js index 9b1c4abd26e..4ece410093b 100644 --- a/server/sonar-web/src/main/js/apps/project-admin/key/views/UpdateKeyConfirmation.js +++ b/server/sonar-web/src/main/js/apps/project-admin/key/views/UpdateKeyConfirmation.js @@ -29,13 +29,8 @@ export default ModalForm.extend({ this.disableForm(); this.showSpinner(); - this.options.onChange(this.options.component.key, this.options.newKey) - .then(() => this.destroy()) - .catch(e => { - parseError(e).then(msg => this.showSingleError(msg)); - this.hideSpinner(); - this.enableForm(); - }); + this.options.onChange(this.options.component.key, this.options.newKey); + this.destroy(); }, serializeData () { diff --git a/server/sonar-web/src/main/js/components/store/globalMessages.js b/server/sonar-web/src/main/js/components/store/globalMessages.js index b2860e09a22..038c74f0132 100644 --- a/server/sonar-web/src/main/js/components/store/globalMessages.js +++ b/server/sonar-web/src/main/js/components/store/globalMessages.js @@ -37,6 +37,13 @@ export const addGlobalErrorMessage = message => export const addGlobalSuccessMessage = message => addGlobalMessage(message, SUCCESS); +const CLOSE_ALL_GLOBAL_MESSAGES = 'CLOSE_ALL_GLOBAL_MESSAGES'; + +export const closeAllGlobalMessages = id => ({ + type: CLOSE_ALL_GLOBAL_MESSAGES, + id +}); + /* Reducer */ const globalMessages = (state = [], action = {}) => { if (action.type === ADD_GLOBAL_MESSAGE) { @@ -47,6 +54,10 @@ const globalMessages = (state = [], action = {}) => { }]; } + if (action.type === CLOSE_ALL_GLOBAL_MESSAGES) { + return []; + } + return state; }; -- 2.39.5