]> source.dussan.org Git - sonarqube.git/commitdiff
rework some modals (#2113)
authorStas Vilchik <stas-vilchik@users.noreply.github.com>
Mon, 29 May 2017 08:49:12 +0000 (10:49 +0200)
committerGitHub <noreply@github.com>
Mon, 29 May 2017 08:49:12 +0000 (10:49 +0200)
19 files changed:
server/sonar-web/src/main/js/app/components/nav/global/GlobalNav.js
server/sonar-web/src/main/js/app/components/nav/global/ShortcutsHelp.js [new file with mode: 0644]
server/sonar-web/src/main/js/app/components/nav/global/ShortcutsHelpView.js [deleted file]
server/sonar-web/src/main/js/app/components/nav/templates/nav-shortcuts-help.hbs [deleted file]
server/sonar-web/src/main/js/apps/background-tasks/components/ScannerContext.js [new file with mode: 0644]
server/sonar-web/src/main/js/apps/background-tasks/components/Stacktrace.js [new file with mode: 0644]
server/sonar-web/src/main/js/apps/background-tasks/components/TaskActions.js
server/sonar-web/src/main/js/apps/background-tasks/views/ScannerContextView.hbs [deleted file]
server/sonar-web/src/main/js/apps/background-tasks/views/ScannerContextView.js [deleted file]
server/sonar-web/src/main/js/apps/background-tasks/views/StacktraceView.hbs [deleted file]
server/sonar-web/src/main/js/apps/background-tasks/views/StacktraceView.js [deleted file]
server/sonar-web/src/main/js/apps/project-admin/deletion/ConfirmationModal.js [deleted file]
server/sonar-web/src/main/js/apps/project-admin/deletion/ConfirmationModalTemplate.hbs [deleted file]
server/sonar-web/src/main/js/apps/project-admin/deletion/Form.js
server/sonar-web/src/main/js/apps/settings/licenses/LicenseChangeForm.js
server/sonar-web/src/main/js/apps/settings/licenses/LicenseRow.js
server/sonar-web/src/main/js/apps/settings/licenses/LicenseValueView.hbs [deleted file]
server/sonar-web/src/main/js/apps/settings/licenses/LicenseValueView.js [deleted file]
server/sonar-web/src/main/js/apps/settings/store/licenses/actions.js

index 86ed0974c5c6e250846df2222d078f4689d313c5..2e2659d7125d9a90550e029af8eccfcfdf5c7cbf 100644 (file)
@@ -23,10 +23,12 @@ import GlobalNavBranding from './GlobalNavBranding';
 import GlobalNavMenu from './GlobalNavMenu';
 import GlobalNavUserContainer from './GlobalNavUserContainer';
 import Search from '../../search/Search';
-import ShortcutsHelpView from './ShortcutsHelpView';
+import ShortcutsHelp from './ShortcutsHelp';
 import { getCurrentUser, getAppState } from '../../../../store/rootReducer';
 
 class GlobalNav extends React.PureComponent {
+  state = { helpOpen: false };
+
   componentDidMount() {
     window.addEventListener('keypress', this.onKeyPress);
   }
@@ -46,13 +48,15 @@ class GlobalNav extends React.PureComponent {
     }
   };
 
-  openHelp = e => {
-    if (e) {
-      e.preventDefault();
-    }
-    new ShortcutsHelpView().render();
+  handleHelpClick = event => {
+    event.preventDefault();
+    this.openHelp();
   };
 
+  openHelp = () => this.setState({ helpOpen: true });
+
+  closeHelp = () => this.setState({ helpOpen: false });
+
   render() {
     /* eslint-disable max-len */
     return (
@@ -65,7 +69,7 @@ class GlobalNav extends React.PureComponent {
           <ul className="nav navbar-nav navbar-right">
             <Search {...this.props} />
             <li>
-              <a className="navbar-help" onClick={this.openHelp} href="#">
+              <a className="navbar-help" onClick={this.handleHelpClick} href="#">
                 <svg width="16" height="16">
                   <g transform="matrix(0.0364583,0,0,0.0364583,1,-0.166667)">
                     <path
@@ -79,6 +83,8 @@ class GlobalNav extends React.PureComponent {
             <GlobalNavUserContainer {...this.props} />
           </ul>
         </div>
+
+        {this.state.helpOpen && <ShortcutsHelp onClose={this.closeHelp} />}
       </nav>
     );
   }
diff --git a/server/sonar-web/src/main/js/app/components/nav/global/ShortcutsHelp.js b/server/sonar-web/src/main/js/app/components/nav/global/ShortcutsHelp.js
new file mode 100644 (file)
index 0000000..273b370
--- /dev/null
@@ -0,0 +1,188 @@
+/*
+ * SonarQube
+ * Copyright (C) 2009-2017 SonarSource SA
+ * mailto:info 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.
+ */
+// @flow
+import React from 'react';
+import Modal from 'react-modal';
+import { Link } from 'react-router';
+import { translate } from '../../../../helpers/l10n';
+
+type Props = {
+  onClose: () => void
+};
+
+export default class ShortcutsHelp extends React.PureComponent {
+  props: Props;
+
+  handleCloseClick = (event: Event) => {
+    event.preventDefault();
+    this.props.onClose();
+  };
+
+  render() {
+    return (
+      <Modal
+        isOpen={true}
+        contentLabel="shortcuts help"
+        className="modal modal-large"
+        overlayClassName="modal-overlay"
+        onRequestClose={this.props.onClose}>
+
+        <div className="modal-head">
+          <h2>{translate('help')}</h2>
+        </div>
+
+        <div className="modal-body modal-container">
+          <div className="spacer-bottom">
+            <a href="http://www.sonarqube.org">{translate('footer.community')}</a>{' - '}
+            <a href="https://redirect.sonarsource.com/doc/home.html">
+              {translate('footer.documentation')}
+            </a>
+            {' - '}
+            <a href="https://redirect.sonarsource.com/doc/community.html">
+              {translate('footer.support')}
+            </a>
+            {' - '}
+            <a href="https://redirect.sonarsource.com/doc/plugin-library.html">
+              {translate('footer.plugins')}
+            </a>
+            {' - '}
+            <Link to="/web_api" onClick={this.props.onClose}>{translate('footer.web_api')}</Link>
+            {' - '}
+            <Link to="/about" onClick={this.props.onClose}>{translate('footer.about')}</Link>
+          </div>
+
+          <h2 className="spacer-top spacer-bottom">{translate('shortcuts.modal_title')}</h2>
+
+          <div className="columns">
+            <div className="column-half">
+              <div className="spacer-bottom">
+                <h3 className="shortcuts-section-title">{translate('shortcuts.section.global')}</h3>
+                <ul className="shortcuts-list">
+                  <li>
+                    <span className="shortcut-button spacer-right">s</span>
+                    {translate('shortcuts.section.global.search')}
+                  </li>
+                  <li>
+                    <span className="shortcut-button spacer-right">?</span>
+                    {translate('shortcuts.section.global.shortcuts')}
+                  </li>
+                </ul>
+              </div>
+
+              <h3 className="shortcuts-section-title">{translate('shortcuts.section.rules')}</h3>
+              <ul className="shortcuts-list">
+                <li>
+                  <span className="shortcut-button little-spacer-right">↑</span>
+                  <span className="shortcut-button spacer-right">↓</span>
+                  {translate('shortcuts.section.rules.navigate_between_rules')}
+                </li>
+                <li>
+                  <span className="shortcut-button spacer-right">→</span>
+                  {translate('shortcuts.section.rules.open_details')}
+                </li>
+                <li>
+                  <span className="shortcut-button spacer-right">←</span>
+                  {translate('shortcuts.section.rules.return_to_list')}
+                </li>
+                <li>
+                  <span className="shortcut-button spacer-right">a</span>
+                  {translate('shortcuts.section.rules.activate')}
+                </li>
+                <li>
+                  <span className="shortcut-button spacer-right">d</span>
+                  {translate('shortcuts.section.rules.deactivate')}
+                </li>
+              </ul>
+            </div>
+
+            <div className="column-half">
+              <h3 className="shortcuts-section-title">{translate('shortcuts.section.issues')}</h3>
+              <ul className="shortcuts-list">
+                <li>
+                  <span className="shortcut-button little-spacer-right">↑</span>
+                  <span className="shortcut-button spacer-right">↓</span>
+                  {translate('shortcuts.section.issues.navigate_between_issues')}
+                </li>
+                <li>
+                  <span className="shortcut-button spacer-right">→</span>
+                  {translate('shortcuts.section.issues.open_details')}
+                </li>
+                <li>
+                  <span className="shortcut-button spacer-right">←</span>
+                  {translate('shortcuts.section.issues.return_to_list')}
+                </li>
+                <li>
+                  <span className="shortcut-button little-spacer-right">alt</span>
+                  <span className="little-spacer-right">+</span>
+                  <span className="shortcut-button little-spacer-right">↑</span>
+                  <span className="shortcut-button spacer-right">↓</span>
+                  {translate('issues.to_navigate_issue_locations')}
+                </li>
+                <li>
+                  <span className="shortcut-button little-spacer-right">alt</span>
+                  <span className="little-spacer-right">+</span>
+                  <span className="shortcut-button little-spacer-right">←</span>
+                  <span className="shortcut-button spacer-right">→</span>
+                  {translate('issues.to_switch_flows')}
+                </li>
+                <li>
+                  <span className="shortcut-button spacer-right">f</span>
+                  {translate('shortcuts.section.issue.do_transition')}
+                </li>
+                <li>
+                  <span className="shortcut-button spacer-right">a</span>
+                  {translate('shortcuts.section.issue.assign')}
+                </li>
+                <li>
+                  <span className="shortcut-button spacer-right">m</span>
+                  {translate('shortcuts.section.issue.assign_to_me')}
+                </li>
+                <li>
+                  <span className="shortcut-button spacer-right">i</span>
+                  {translate('shortcuts.section.issue.change_severity')}
+                </li>
+                <li>
+                  <span className="shortcut-button spacer-right">c</span>
+                  {translate('shortcuts.section.issue.comment')}
+                </li>
+                <li>
+                  <span className="shortcut-button little-spacer-right">ctrl</span>
+                  <span className="shortcut-button spacer-right">enter</span>
+                  {translate('shortcuts.section.issue.submit_comment')}
+                </li>
+                <li>
+                  <span className="shortcut-button spacer-right">t</span>
+                  {translate('shortcuts.section.issue.change_tags')}
+                </li>
+              </ul>
+            </div>
+          </div>
+        </div>
+
+        <div className="modal-foot">
+          <a className="js-modal-close" href="#" onClick={this.handleCloseClick}>
+            {translate('close')}
+          </a>
+        </div>
+
+      </Modal>
+    );
+  }
+}
diff --git a/server/sonar-web/src/main/js/app/components/nav/global/ShortcutsHelpView.js b/server/sonar-web/src/main/js/app/components/nav/global/ShortcutsHelpView.js
deleted file mode 100644 (file)
index 893e493..0000000
+++ /dev/null
@@ -1,26 +0,0 @@
-/*
- * SonarQube
- * Copyright (C) 2009-2017 SonarSource SA
- * mailto:info 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 ModalView from '../../../../components/common/modals';
-import ShortcutsHelpTemplate from '../templates/nav-shortcuts-help.hbs';
-
-export default ModalView.extend({
-  className: 'modal modal-large',
-  template: ShortcutsHelpTemplate
-});
diff --git a/server/sonar-web/src/main/js/app/components/nav/templates/nav-shortcuts-help.hbs b/server/sonar-web/src/main/js/app/components/nav/templates/nav-shortcuts-help.hbs
deleted file mode 100644 (file)
index 00f5519..0000000
+++ /dev/null
@@ -1,70 +0,0 @@
-<div class="modal-head">
-  <h2>{{t 'help'}}</h2>
-</div>
-
-<div class="modal-body modal-container">
-  <div class="spacer-bottom">
-    <a href="http://www.sonarqube.org">Community</a> -
-    <a href="https://redirect.sonarsource.com/doc/home.html">Documentation</a> -
-    <a href="https://redirect.sonarsource.com/doc/community.html">Get Support</a> -
-    <a href="https://redirect.sonarsource.com/doc/plugin-library.html">Plugins</a> -
-    <a href="{{link '/web_api'}}">Web API</a> -
-    <a href="{{link '/about'}}">About</a>
-  </div>
-
-  <h2 class="spacer-top spacer-bottom">{{t 'shortcuts.modal_title'}}</h2>
-
-  <div class="columns">
-    <div class="column-half">
-      <div class="spacer-bottom">
-        <h3 class="shortcuts-section-title">{{t 'shortcuts.section.global'}}</h3>
-        <ul class="shortcuts-list">
-          <li><span class="shortcut-button">s</span> &nbsp;&nbsp; {{t 'shortcuts.section.global.search'}}</li>
-          <li><span class="shortcut-button">?</span> &nbsp;&nbsp; {{t 'shortcuts.section.global.shortcuts'}}</li>
-        </ul>
-      </div>
-
-      <h3 class="shortcuts-section-title">{{t 'shortcuts.section.rules'}}</h3>
-      <ul class="shortcuts-list">
-        <li><span class="shortcut-button">&uarr;</span> <span class="shortcut-button">&darr;</span> &nbsp;&nbsp; {{t 'shortcuts.section.rules.navigate_between_rules'}}</li>
-        <li><span class="shortcut-button">&rarr;</span> &nbsp;&nbsp; {{t 'shortcuts.section.rules.open_details'}}</li>
-        <li><span class="shortcut-button">&larr;</span> &nbsp;&nbsp; {{t 'shortcuts.section.rules.return_to_list'}}</li>
-        <li><span class="shortcut-button">a</span> &nbsp;&nbsp; {{t 'shortcuts.section.rules.activate'}}</li>
-        <li><span class="shortcut-button">d</span> &nbsp;&nbsp; {{t 'shortcuts.section.rules.deactivate'}}</li>
-      </ul>
-    </div>
-
-    <div class="column-half">
-      <h3 class="shortcuts-section-title">{{t 'shortcuts.section.issues'}}</h3>
-      <ul class="shortcuts-list">
-        <li><span class="shortcut-button">&uarr;</span> <span class="shortcut-button">&darr;</span> &nbsp;&nbsp; {{t 'shortcuts.section.issues.navigate_between_issues'}}
-        </li>
-        <li><span class="shortcut-button">&rarr;</span> &nbsp;&nbsp; {{t 'shortcuts.section.issues.open_details'}}</li>
-        <li><span class="shortcut-button">&larr;</span> &nbsp;&nbsp; {{t 'shortcuts.section.issues.return_to_list'}}</li>
-        <li>
-          <span class="shortcut-button">alt</span>
-          <span class=>+</span>
-          <span class="shortcut-button">↑</span>
-          <span class="shortcut-button">↓</span> {{t 'issues.to_navigate_issue_locations'}}
-        </li>
-        <li>
-          <span class="shortcut-button">alt</span>
-          <span class=>+</span>
-          <span class="shortcut-button">←</span>
-          <span class="shortcut-button">→</span> {{t 'issues.to_switch_flows'}}
-        </li>
-        <li><span class="shortcut-button">f</span> &nbsp;&nbsp; {{t 'shortcuts.section.issue.do_transition'}}</li>
-        <li><span class="shortcut-button">a</span> &nbsp;&nbsp; {{t 'shortcuts.section.issue.assign'}}</li>
-        <li><span class="shortcut-button">m</span> &nbsp;&nbsp; {{t 'shortcuts.section.issue.assign_to_me'}}</li>
-        <li><span class="shortcut-button">i</span> &nbsp;&nbsp; {{t 'shortcuts.section.issue.change_severity'}}</li>
-        <li><span class="shortcut-button">c</span> &nbsp;&nbsp; {{t 'shortcuts.section.issue.comment'}}</li>
-        <li><span class="shortcut-button">ctrl</span> + <span class="shortcut-button">enter</span> &nbsp;&nbsp; {{t 'shortcuts.section.issue.submit_comment'}}</li>
-        <li><span class="shortcut-button">t</span> &nbsp;&nbsp; {{t 'shortcuts.section.issue.change_tags'}}</li>
-      </ul>
-    </div>
-  </div>
-</div>
-
-<div class="modal-foot">
-  <a class="js-modal-close" href="#">{{t 'close'}}</a>
-</div>
\ No newline at end of file
diff --git a/server/sonar-web/src/main/js/apps/background-tasks/components/ScannerContext.js b/server/sonar-web/src/main/js/apps/background-tasks/components/ScannerContext.js
new file mode 100644 (file)
index 0000000..f836445
--- /dev/null
@@ -0,0 +1,102 @@
+/*
+ * SonarQube
+ * Copyright (C) 2009-2017 SonarSource SA
+ * mailto:info 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.
+ */
+// @flow
+import React from 'react';
+import Modal from 'react-modal';
+import { getTask } from '../../../api/ce';
+import { translate } from '../../../helpers/l10n';
+
+type Props = {
+  onClose: () => void,
+  task: { componentName: string, id: string, type: string }
+};
+
+type State = {
+  scannerContext: ?string
+};
+
+export default class ScannerContext extends React.PureComponent {
+  mounted: boolean;
+  props: Props;
+  state: State = {
+    scannerContext: null
+  };
+
+  componentDidMount() {
+    this.mounted = true;
+    this.loadScannerContext();
+  }
+
+  componentWillUnmount() {
+    this.mounted = false;
+  }
+
+  loadScannerContext() {
+    getTask(this.props.task.id, ['scannerContext']).then(task => {
+      if (this.mounted) {
+        this.setState({ scannerContext: task.scannerContext });
+      }
+    });
+  }
+
+  handleCloseClick = (event: Event) => {
+    event.preventDefault();
+    this.props.onClose();
+  };
+
+  render() {
+    const { task } = this.props;
+    const { scannerContext } = this.state;
+
+    return (
+      <Modal
+        isOpen={true}
+        contentLabel="scanner context"
+        className="modal modal-large"
+        overlayClassName="modal-overlay"
+        onRequestClose={this.props.onClose}>
+
+        <div className="modal-head">
+          <h2>
+            {translate('background_tasks.scanner_context')}
+            {': '}
+            {task.componentName}
+            {' ['}
+            {translate('background_task.type', task.type)}
+            {']'}
+          </h2>
+        </div>
+
+        <div className="modal-body modal-container">
+          {scannerContext != null
+            ? <pre className="js-task-scanner-context">{scannerContext}</pre>
+            : <i className="spinner" />}
+        </div>
+
+        <div className="modal-foot">
+          <a href="#" className="js-modal-close" onClick={this.handleCloseClick}>
+            {translate('close')}
+          </a>
+        </div>
+
+      </Modal>
+    );
+  }
+}
diff --git a/server/sonar-web/src/main/js/apps/background-tasks/components/Stacktrace.js b/server/sonar-web/src/main/js/apps/background-tasks/components/Stacktrace.js
new file mode 100644 (file)
index 0000000..d6da81e
--- /dev/null
@@ -0,0 +1,114 @@
+/*
+ * SonarQube
+ * Copyright (C) 2009-2017 SonarSource SA
+ * mailto:info 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.
+ */
+// @flow
+import React from 'react';
+import Modal from 'react-modal';
+import { getTask } from '../../../api/ce';
+import { translate } from '../../../helpers/l10n';
+
+type Props = {
+  onClose: () => void,
+  task: { componentName: string, errorMessage: string, id: string, type: string }
+};
+
+type State = {
+  loading: boolean,
+  stacktrace: ?string
+};
+
+export default class Stacktrace extends React.PureComponent {
+  mounted: boolean;
+  props: Props;
+  state: State = {
+    loading: true,
+    stacktrace: null
+  };
+
+  componentDidMount() {
+    this.mounted = true;
+    this.loadStacktrace();
+  }
+
+  componentWillUnmount() {
+    this.mounted = false;
+  }
+
+  loadStacktrace() {
+    getTask(this.props.task.id, ['stacktrace']).then(task => {
+      if (this.mounted) {
+        this.setState({ loading: false, stacktrace: task.errorStacktrace });
+      }
+    });
+  }
+
+  handleCloseClick = (event: Event) => {
+    event.preventDefault();
+    this.props.onClose();
+  };
+
+  render() {
+    const { task } = this.props;
+    const { loading, stacktrace } = this.state;
+
+    return (
+      <Modal
+        isOpen={true}
+        contentLabel="stacktrace"
+        className="modal modal-large"
+        overlayClassName="modal-overlay"
+        onRequestClose={this.props.onClose}>
+
+        <div className="modal-head">
+          <h2>
+            {translate('background_tasks.error_stacktrace')}
+            {': '}
+            {task.componentName}
+            {' ['}
+            {translate('background_task.type', task.type)}
+            {']'}
+          </h2>
+        </div>
+
+        <div className="modal-body modal-container">
+          {loading
+            ? <i className="spinner" />
+            : stacktrace
+                ? <div>
+                    <h4 className="spacer-bottom">
+                      {translate('background_tasks.error_stacktrace')}
+                    </h4>
+                    <pre className="js-task-stacktrace">{stacktrace}</pre>
+                  </div>
+                : <div>
+                    <h4 className="spacer-bottom">{translate('background_tasks.error_message')}</h4>
+                    <pre className="js-task-error-message">{task.errorMessage}</pre>
+                  </div>}
+        </div>
+
+        <div className="modal-foot">
+          <a href="#" className="js-modal-close" onClick={this.handleCloseClick}>
+            {translate('close')}
+          </a>
+        </div>
+
+      </Modal>
+    );
+  }
+}
index 1acde211d5f90753eaf0c874979b0d149e2d1d32..03b8be8455d15dfa2a897e46afe7126feb433d89 100644 (file)
  * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
  */
 import React from 'react';
-import ScannerContextView from '../views/ScannerContextView';
-import StacktraceView from '../views/StacktraceView';
+import ScannerContext from './ScannerContext';
+import Stacktrace from './Stacktrace';
 import { STATUSES } from './../constants';
 import { translate, translateWithParameters } from '../../../helpers/l10n';
 
 export default class TaskActions extends React.PureComponent {
+  state = {
+    scannerContextOpen: false,
+    stacktraceOpen: false
+  };
+
   handleFilterClick(e) {
     e.preventDefault();
     this.props.onFilterTask(this.props.task);
@@ -36,14 +41,18 @@ export default class TaskActions extends React.PureComponent {
 
   handleShowScannerContextClick(e) {
     e.preventDefault();
-    new ScannerContextView({ task: this.props.task }).render();
+    this.setState({ scannerContextOpen: true });
   }
 
+  closeScannerContext = () => this.setState({ scannerContextOpen: false });
+
   handleShowStacktraceClick(e) {
     e.preventDefault();
-    new StacktraceView({ task: this.props.task }).render();
+    this.setState({ stacktraceOpen: true });
   }
 
+  closeStacktrace = () => this.setState({ stacktraceOpen: false });
+
   render() {
     const { component, task } = this.props;
 
@@ -102,6 +111,11 @@ export default class TaskActions extends React.PureComponent {
               </li>}
           </ul>
         </div>
+
+        {this.state.scannerContextOpen &&
+          <ScannerContext onClose={this.closeScannerContext} task={task} />}
+
+        {this.state.stacktraceOpen && <Stacktrace onClose={this.closeStacktrace} task={task} />}
       </td>
     );
   }
diff --git a/server/sonar-web/src/main/js/apps/background-tasks/views/ScannerContextView.hbs b/server/sonar-web/src/main/js/apps/background-tasks/views/ScannerContextView.hbs
deleted file mode 100644 (file)
index d1de3da..0000000
+++ /dev/null
@@ -1,17 +0,0 @@
-<form id="deactivate-user-form" autocomplete="off">
-  <div class="modal-head">
-    <h2>{{t 'background_tasks.scanner_context'}}: {{task.componentName}} [{{t 'background_task.type' task.type}}]</h2>
-  </div>
-  <div class="modal-body modal-container">
-    <div class="js-modal-messages"></div>
-
-    {{#if scannerContext}}
-      <pre class="js-task-scanner-context">{{scannerContext}}</pre>
-    {{else}}
-      <i class="spinner"></i>
-    {{/if}}
-  </div>
-  <div class="modal-foot">
-    <a href="#" class="js-modal-close">{{t 'close'}}</a>
-  </div>
-</form>
diff --git a/server/sonar-web/src/main/js/apps/background-tasks/views/ScannerContextView.js b/server/sonar-web/src/main/js/apps/background-tasks/views/ScannerContextView.js
deleted file mode 100644 (file)
index ef35cf5..0000000
+++ /dev/null
@@ -1,46 +0,0 @@
-/*
- * SonarQube
- * Copyright (C) 2009-2017 SonarSource SA
- * mailto:info 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 Modal from '../../../components/common/modals';
-import Template from './ScannerContextView.hbs';
-import { getTask } from '../../../api/ce';
-
-export default Modal.extend({
-  template: Template,
-  className: 'modal modal-large',
-
-  initialize() {
-    this.scannerContext = null;
-    this.loadScannerContext();
-  },
-
-  loadScannerContext() {
-    getTask(this.options.task.id, ['scannerContext']).then(task => {
-      this.scannerContext = task.scannerContext;
-      this.render();
-    });
-  },
-
-  serializeData() {
-    return {
-      task: this.options.task,
-      scannerContext: this.scannerContext
-    };
-  }
-});
diff --git a/server/sonar-web/src/main/js/apps/background-tasks/views/StacktraceView.hbs b/server/sonar-web/src/main/js/apps/background-tasks/views/StacktraceView.hbs
deleted file mode 100644 (file)
index 21ce604..0000000
+++ /dev/null
@@ -1,23 +0,0 @@
-<form id="deactivate-user-form" autocomplete="off">
-  <div class="modal-head">
-    <h2>{{t 'background_tasks.error_stacktrace'}}: {{task.componentName}} [{{t 'background_task.type' task.type}}]</h2>
-  </div>
-  <div class="modal-body modal-container">
-    <div class="js-modal-messages"></div>
-
-    {{#if loaded}}
-      {{#if stacktrace}}
-        <h4 class="spacer-bottom">{{t 'background_tasks.error_stacktrace'}}</h4>
-        <pre class="js-task-stacktrace">{{stacktrace}}</pre>
-      {{else}}
-        <h4 class="spacer-bottom">{{t 'background_tasks.error_message'}}</h4>
-        <pre class="js-task-error-message">{{task.errorMessage}}</pre>
-      {{/if}}
-    {{else}}
-      <i class="spinner"></i>
-    {{/if}}
-  </div>
-  <div class="modal-foot">
-    <a href="#" class="js-modal-close">{{t 'close'}}</a>
-  </div>
-</form>
diff --git a/server/sonar-web/src/main/js/apps/background-tasks/views/StacktraceView.js b/server/sonar-web/src/main/js/apps/background-tasks/views/StacktraceView.js
deleted file mode 100644 (file)
index 866d9a4..0000000
+++ /dev/null
@@ -1,49 +0,0 @@
-/*
- * SonarQube
- * Copyright (C) 2009-2017 SonarSource SA
- * mailto:info 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 Modal from '../../../components/common/modals';
-import Template from './StacktraceView.hbs';
-import { getTask } from '../../../api/ce';
-
-export default Modal.extend({
-  template: Template,
-  className: 'modal modal-large',
-
-  initialize() {
-    this.loaded = false;
-    this.stacktrace = null;
-    this.loadStacktrace();
-  },
-
-  loadStacktrace() {
-    getTask(this.options.task.id, ['stacktrace']).then(task => {
-      this.loaded = true;
-      this.stacktrace = task.errorStacktrace;
-      this.render();
-    });
-  },
-
-  serializeData() {
-    return {
-      task: this.options.task,
-      stacktrace: this.stacktrace,
-      loaded: this.loaded
-    };
-  }
-});
diff --git a/server/sonar-web/src/main/js/apps/project-admin/deletion/ConfirmationModal.js b/server/sonar-web/src/main/js/apps/project-admin/deletion/ConfirmationModal.js
deleted file mode 100644 (file)
index 273850e..0000000
+++ /dev/null
@@ -1,48 +0,0 @@
-/*
- * SonarQube
- * Copyright (C) 2009-2017 SonarSource SA
- * mailto:info 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 ModalForm from '../../../components/common/modal-form';
-import Template from './ConfirmationModalTemplate.hbs';
-import { deleteProject } from '../../../api/components';
-
-export default ModalForm.extend({
-  template: Template,
-
-  onFormSubmit() {
-    ModalForm.prototype.onFormSubmit.apply(this, arguments);
-    this.disableForm();
-    this.showSpinner();
-
-    deleteProject(this.options.project.key)
-      .then(() => {
-        this.trigger('done');
-      })
-      .catch(function(e) {
-        e.response.json().then(r => {
-          this.hideSpinner();
-          this.showErrors(r.errors, r.warnings);
-          this.enableForm();
-        });
-      });
-  },
-
-  serializeData() {
-    return { project: this.options.project };
-  }
-});
diff --git a/server/sonar-web/src/main/js/apps/project-admin/deletion/ConfirmationModalTemplate.hbs b/server/sonar-web/src/main/js/apps/project-admin/deletion/ConfirmationModalTemplate.hbs
deleted file mode 100644 (file)
index 749e408..0000000
+++ /dev/null
@@ -1,14 +0,0 @@
-<form id="deactivate-user-form" autocomplete="off">
-  <div class="modal-head">
-    <h2>{{t 'qualifiers.delete.TRK'}}</h2>
-  </div>
-  <div class="modal-body">
-    <div class="js-modal-messages"></div>
-    {{tp 'project_deletion.delete_resource_confirmation' project.name}}
-  </div>
-  <div class="modal-foot">
-    <i class="js-modal-spinner spinner spacer-right hidden"></i>
-    <button id="delete-project-confirm" class="button-red">{{t 'delete'}}</button>
-    <a href="#" class="js-modal-close">{{t 'cancel'}}</a>
-  </div>
-</form>
index 1a848accaee334978687830731846d53a495c90f..ac8ca55598b2e6d49712d2d8efcab63d2295cee4 100644 (file)
  * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
  */
 import React from 'react';
-import ConfirmationModal from './ConfirmationModal';
-import { translate } from '../../../helpers/l10n';
+import Modal from 'react-modal';
+import { deleteProject } from '../../../api/components';
+import { translate, translateWithParameters } from '../../../helpers/l10n';
 
 export default class Form extends React.PureComponent {
   static propTypes = {
     component: React.PropTypes.object.isRequired
   };
 
-  handleDelete(e) {
-    e.preventDefault();
-    new ConfirmationModal({ project: this.props.component })
-      .on('done', () => {
-        window.location = window.baseUrl + '/';
-      })
-      .render();
+  static contextTypes = {
+    router: React.PropTypes.object
+  };
+
+  state = { loading: false, modalOpen: false };
+
+  componentDidMount() {
+    this.mounted = true;
+  }
+
+  componentWillUnmount() {
+    this.mounted = false;
   }
 
+  handleDeleteClick = event => {
+    event.preventDefault();
+    this.setState({ modalOpen: true });
+  };
+
+  closeModal = () => this.setState({ modalOpen: false });
+
+  stopLoading = () => {
+    if (this.mounted) {
+      this.setState({ loading: false });
+    }
+  };
+
+  handleSubmit = event => {
+    event.preventDefault();
+    this.setState({ loading: true });
+    deleteProject(this.props.component.key)
+      .then(() => this.context.router.replace('/'))
+      .catch(this.stopLoading);
+  };
+
+  handleCloseClick = (event: Event) => {
+    event.preventDefault();
+    this.closeModal();
+  };
+
   render() {
+    const { component } = this.props;
+
     return (
-      <form onSubmit={this.handleDelete.bind(this)}>
-        <button id="delete-project" className="button-red">
+      <div>
+        <button id="delete-project" className="button-red" onClick={this.handleDeleteClick}>
           {translate('delete')}
         </button>
-      </form>
+
+        {this.state.modalOpen &&
+          <Modal
+            isOpen={true}
+            contentLabel="project deletion"
+            className="modal"
+            overlayClassName="modal-overlay"
+            onRequestClose={this.closeModal}>
+            <form onSubmit={this.handleSubmit}>
+              <div className="modal-head">
+                <h2>{translate('qualifiers.delete.TRK')}</h2>
+              </div>
+              <div className="modal-body">
+                <div className="js-modal-messages" />
+                {translateWithParameters(
+                  'project_deletion.delete_resource_confirmation',
+                  component.name
+                )}
+              </div>
+              <div className="modal-foot">
+                {this.state.loading && <i className="js-modal-spinner spinner spacer-right" />}
+                <button
+                  id="delete-project-confirm"
+                  className="button-red"
+                  disabled={this.state.loading}>
+                  {translate('delete')}
+                </button>
+                <a href="#" className="js-modal-close" onClick={this.handleCloseClick}>
+                  {translate('cancel')}
+                </a>
+              </div>
+            </form>
+          </Modal>}
+      </div>
     );
   }
 }
index e3e443e51b6228b728e1f4aa6b7cac350335cda5..ff068c49d98f876f97403a7b0a1089046af7e61c 100644 (file)
@@ -18,8 +18,8 @@
  * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
  */
 import React from 'react';
-import LicenseValueView from './LicenseValueView';
-import { translate } from '../../../helpers/l10n';
+import Modal from 'react-modal';
+import { translate, translateWithParameters } from '../../../helpers/l10n';
 
 export default class LicenseChangeForm extends React.PureComponent {
   static propTypes = {
@@ -27,23 +27,80 @@ export default class LicenseChangeForm extends React.PureComponent {
     onChange: React.PropTypes.func.isRequired
   };
 
+  state = {
+    loading: false,
+    modalOpen: false
+  };
+
   onClick(e) {
     e.preventDefault();
     e.target.blur();
+    this.setState({ modalOpen: true });
+  }
 
-    const { license, onChange } = this.props;
+  closeModal = () => this.setState({ modalOpen: false });
 
-    new LicenseValueView({
-      productName: license.name || license.key,
-      value: license.value,
-      onChange
-    }).render();
-  }
+  handleSubmit = event => {
+    event.preventDefault();
+    if (this.textarea) {
+      const { value } = this.textarea;
+      this.setState({ loading: true });
+      this.props
+        .onChange(value)
+        .then(
+          () => this.setState({ loading: false, modalOpen: false }),
+          () => this.setState({ loading: false })
+        );
+    }
+  };
+
+  handleCancelClick = event => {
+    event.preventDefault();
+    this.closeModal();
+  };
 
   render() {
+    const { license } = this.props;
+    const productName = license.name || license.key;
+
     return (
       <button className="js-change" onClick={e => this.onClick(e)}>
         {translate('update_verb')}
+
+        {this.state.modalOpen &&
+          <Modal
+            isOpen={true}
+            contentLabel="license update"
+            className="modal"
+            overlayClassName="modal-overlay"
+            onRequestClose={this.closeModal}>
+            <form onSubmit={this.handleSubmit}>
+              <div className="modal-head">
+                <h2>{translateWithParameters('licenses.update_license_for_x', productName)}</h2>
+              </div>
+              <div className="modal-body">
+                <label htmlFor="license-input">{translate('licenses.license_input_label')}</label>
+                <textarea
+                  autoFocus={true}
+                  className="width-100 spacer-top"
+                  ref={node => (this.textarea = node)}
+                  rows="7"
+                  id="license-input"
+                  defaultValue={license.value}
+                />
+                <div className="spacer-top note">{translate('licenses.license_input_note')}</div>
+              </div>
+              <div className="modal-foot">
+                {this.state.loading && <i className="js-modal-spinner spinner spacer-right" />}
+                <button className="js-modal-submit" disabled={this.state.loading}>
+                  {translate('save')}
+                </button>
+                <a href="#" className="js-modal-close" onClick={this.handleCancelClick}>
+                  {translate('cancel')}
+                </a>
+              </div>
+            </form>
+          </Modal>}
       </button>
     );
   }
index 94f12ae77209ad9fc06882755593c4f3afd0bae7..9a5a332ac200633390879d5c42e8640692c45c95 100644 (file)
@@ -28,11 +28,7 @@ export default class LicenseRow extends React.PureComponent {
     setLicense: React.PropTypes.func.isRequired
   };
 
-  handleSet(value) {
-    return this.props.setLicense(this.props.license.key, value).catch(() => {
-      /* do nothing */
-    });
-  }
+  handleSet = value => this.props.setLicense(this.props.license.key, value);
 
   render() {
     const { license } = this.props;
@@ -59,7 +55,7 @@ export default class LicenseRow extends React.PureComponent {
           </div>
         </td>
         <td className="text-right">
-          <LicenseChangeForm license={license} onChange={value => this.handleSet(value)} />
+          <LicenseChangeForm license={license} onChange={this.handleSet} />
         </td>
       </tr>
     );
diff --git a/server/sonar-web/src/main/js/apps/settings/licenses/LicenseValueView.hbs b/server/sonar-web/src/main/js/apps/settings/licenses/LicenseValueView.hbs
deleted file mode 100644 (file)
index c4aab7e..0000000
+++ /dev/null
@@ -1,16 +0,0 @@
-<form>
-  <div class="modal-head">
-    <h2>{{tp 'licenses.update_license_for_x' productName}}</h2>
-  </div>
-  <div class="modal-body">
-    <div class="js-modal-messages"></div>
-    <label for="license-input">{{t 'licenses.license_input_label'}}</label>
-    <textarea class="width-100 spacer-top" rows="7" id="license-input">{{value}}</textarea>
-    <div class="spacer-top note">{{t 'licenses.license_input_note'}}</div>
-  </div>
-  <div class="modal-foot">
-    <i class="js-modal-spinner spinner spacer-right hidden"></i>
-    <button class="js-modal-submit">{{t 'save'}}</button>
-    <a href="#" class="js-modal-close">{{t 'cancel'}}</a>
-  </div>
-</form>
diff --git a/server/sonar-web/src/main/js/apps/settings/licenses/LicenseValueView.js b/server/sonar-web/src/main/js/apps/settings/licenses/LicenseValueView.js
deleted file mode 100644 (file)
index 22b4e3a..0000000
+++ /dev/null
@@ -1,41 +0,0 @@
-/*
- * SonarQube
- * Copyright (C) 2009-2017 SonarSource SA
- * mailto:info 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 ModalForm from '../../../components/common/modal-form';
-import Template from './LicenseValueView.hbs';
-
-export default ModalForm.extend({
-  template: Template,
-
-  onFormSubmit() {
-    ModalForm.prototype.onFormSubmit.apply(this, arguments);
-    this.disableForm();
-    this.showSpinner();
-
-    const value = this.$('textarea').val();
-    this.options.onChange(value).then(() => this.destroy());
-  },
-
-  serializeData() {
-    return {
-      productName: this.options.productName,
-      value: this.options.value
-    };
-  }
-});
index c24cae2d6d2b115aa42eaec60f4e75994f83a140..9ef043c73c75e91159eefe15b06d3b1528be3460 100644 (file)
@@ -56,7 +56,7 @@ export const setLicense = (key, value) => dispatch => {
   const request = value ? licenses.setLicense(key, value) : licenses.resetLicense(key);
 
   return request
-    .then(() => {
+    .then(() =>
       licenses.getLicenses().then(licenses => {
         dispatch(receiveLicenses(licenses));
         if (isLicenseFromListInvalid(licenses, key)) {
@@ -64,7 +64,7 @@ export const setLicense = (key, value) => dispatch => {
         } else {
           dispatch(addGlobalSuccessMessage(translate('licenses.success_message')));
         }
-      });
-    })
+      })
+    )
     .catch(handleError(dispatch));
 };