aboutsummaryrefslogtreecommitdiffstats
path: root/server/sonar-web
diff options
context:
space:
mode:
authorGrégoire Aubert <gregoire.aubert@sonarsource.com>2017-11-30 16:05:46 +0100
committerJulien Lancelot <julien.lancelot@sonarsource.com>2017-12-14 17:03:35 +0100
commitc37f8a7202925bec2d9a27961ea10f125c259c91 (patch)
tree9930cb4e5d1e7a003c3ac1f1082b2cade3b375e1 /server/sonar-web
parent42b5ea685c8ee46cf05625879300aa3eb3529897 (diff)
downloadsonarqube-c37f8a7202925bec2d9a27961ea10f125c259c91.tar.gz
sonarqube-c37f8a7202925bec2d9a27961ea10f125c259c91.zip
SONAR-10151 Refactor Quality Gates modals to Typescript
Diffstat (limited to 'server/sonar-web')
-rw-r--r--server/sonar-web/src/main/js/api/quality-gates.ts33
-rw-r--r--server/sonar-web/src/main/js/apps/quality-gates/components/CopyQualityGateForm.tsx134
-rw-r--r--server/sonar-web/src/main/js/apps/quality-gates/components/CreateQualityGateForm.tsx127
-rw-r--r--server/sonar-web/src/main/js/apps/quality-gates/components/DeleteQualityGateForm.tsx106
-rw-r--r--server/sonar-web/src/main/js/apps/quality-gates/components/Details.js57
-rw-r--r--server/sonar-web/src/main/js/apps/quality-gates/components/DetailsHeader.tsx (renamed from server/sonar-web/src/main/js/apps/quality-gates/components/DetailsHeader.js)75
-rw-r--r--server/sonar-web/src/main/js/apps/quality-gates/components/List.js2
-rw-r--r--server/sonar-web/src/main/js/apps/quality-gates/components/ListHeader.js42
-rw-r--r--server/sonar-web/src/main/js/apps/quality-gates/components/ListHeader.tsx65
-rw-r--r--server/sonar-web/src/main/js/apps/quality-gates/components/QualityGatesApp.js16
-rw-r--r--server/sonar-web/src/main/js/apps/quality-gates/components/RenameQualityGateForm.tsx125
-rw-r--r--server/sonar-web/src/main/js/apps/quality-gates/templates/quality-gate-form.hbs20
-rw-r--r--server/sonar-web/src/main/js/apps/quality-gates/templates/quality-gates-delete.hbs17
-rw-r--r--server/sonar-web/src/main/js/apps/quality-gates/views/copy-view.js53
-rw-r--r--server/sonar-web/src/main/js/apps/quality-gates/views/create-view.js52
-rw-r--r--server/sonar-web/src/main/js/apps/quality-gates/views/delete-view.js52
-rw-r--r--server/sonar-web/src/main/js/apps/quality-gates/views/gate-projects-view.js15
-rw-r--r--server/sonar-web/src/main/js/apps/quality-gates/views/rename-view.js53
18 files changed, 667 insertions, 377 deletions
diff --git a/server/sonar-web/src/main/js/api/quality-gates.ts b/server/sonar-web/src/main/js/api/quality-gates.ts
index d2402fcdb46..90b03cbe338 100644
--- a/server/sonar-web/src/main/js/api/quality-gates.ts
+++ b/server/sonar-web/src/main/js/api/quality-gates.ts
@@ -52,31 +52,42 @@ export function fetchQualityGates(): Promise<{
return getJSON('/api/qualitygates/list').catch(throwGlobalError);
}
-export function fetchQualityGate(id: string): Promise<QualityGate> {
+export function fetchQualityGate(id: number): Promise<QualityGate> {
return getJSON('/api/qualitygates/show', { id }).catch(throwGlobalError);
}
-export function createQualityGate(name: string): Promise<any> {
- return postJSON('/api/qualitygates/create', { name });
+export function createQualityGate(data: {
+ name: string;
+ organization?: string;
+}): Promise<QualityGate> {
+ return postJSON('/api/qualitygates/create', data).catch(throwGlobalError);
}
-export function deleteQualityGate(id: string): Promise<void> {
+export function deleteQualityGate(id: number): Promise<void> {
return post('/api/qualitygates/destroy', { id });
}
-export function renameQualityGate(id: string, name: string): Promise<void> {
- return post('/api/qualitygates/rename', { id, name });
+export function renameQualityGate(data: {
+ id: number;
+ name: string;
+ organization?: string;
+}): Promise<void | Response> {
+ return post('/api/qualitygates/rename', data).catch(throwGlobalError);
}
-export function copyQualityGate(id: string, name: string): Promise<any> {
- return postJSON('/api/qualitygates/copy', { id, name });
+export function copyQualityGate(data: {
+ id: number;
+ name: string;
+ organization?: string;
+}): Promise<QualityGate> {
+ return postJSON('/api/qualitygates/copy', data).catch(throwGlobalError);
}
-export function setQualityGateAsDefault(id: string): Promise<void | Response> {
+export function setQualityGateAsDefault(id: number): Promise<void | Response> {
return post('/api/qualitygates/set_as_default', { id }).catch(throwGlobalError);
}
-export function createCondition(gateId: string, condition: RequestData): Promise<any> {
+export function createCondition(gateId: number, condition: RequestData): Promise<any> {
return postJSON('/api/qualitygates/create_condition', { ...condition, gateId });
}
@@ -84,7 +95,7 @@ export function updateCondition(condition: RequestData): Promise<any> {
return postJSON('/api/qualitygates/update_condition', condition);
}
-export function deleteCondition(id: string): Promise<void> {
+export function deleteCondition(id: number): Promise<void> {
return post('/api/qualitygates/delete_condition', { id });
}
diff --git a/server/sonar-web/src/main/js/apps/quality-gates/components/CopyQualityGateForm.tsx b/server/sonar-web/src/main/js/apps/quality-gates/components/CopyQualityGateForm.tsx
new file mode 100644
index 00000000000..ceeb416cc63
--- /dev/null
+++ b/server/sonar-web/src/main/js/apps/quality-gates/components/CopyQualityGateForm.tsx
@@ -0,0 +1,134 @@
+/*
+ * 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 * as React from 'react';
+import * as PropTypes from 'prop-types';
+import Modal from '../../../components/controls/Modal';
+import { copyQualityGate, QualityGate } from '../../../api/quality-gates';
+import { getQualityGateUrl } from '../../../helpers/urls';
+import { translate } from '../../../helpers/l10n';
+
+interface Props {
+ qualityGate: QualityGate;
+ onCopy: (newQualityGate: QualityGate) => void;
+ onClose: () => void;
+ organization?: string;
+}
+
+interface State {
+ loading: boolean;
+ name: string;
+}
+
+export default class CopyQualityGateForm extends React.PureComponent<Props, State> {
+ mounted: boolean;
+
+ static contextTypes = {
+ router: PropTypes.object
+ };
+
+ constructor(props: Props) {
+ super(props);
+ this.state = { loading: false, name: props.qualityGate.name };
+ }
+
+ componentDidMount() {
+ this.mounted = true;
+ }
+
+ componentWillUnmount() {
+ this.mounted = false;
+ }
+
+ handleCancelClick = (event: React.SyntheticEvent<HTMLElement>) => {
+ event.preventDefault();
+ this.props.onClose();
+ };
+
+ handleNameChange = (event: React.SyntheticEvent<HTMLInputElement>) => {
+ this.setState({ name: event.currentTarget.value });
+ };
+
+ handleFormSubmit = (event: React.SyntheticEvent<HTMLFormElement>) => {
+ event.preventDefault();
+ const { qualityGate, organization } = this.props;
+ const { name } = this.state;
+ if (name) {
+ this.setState({ loading: true });
+ copyQualityGate({ id: qualityGate.id, name, organization }).then(
+ qualityGate => {
+ this.props.onCopy(qualityGate);
+ this.props.onClose();
+ this.context.router.push(
+ getQualityGateUrl(String(qualityGate.id), this.props.organization)
+ );
+ },
+ () => {
+ if (this.mounted) {
+ this.setState({ loading: false });
+ }
+ }
+ );
+ }
+ };
+
+ render() {
+ const { qualityGate } = this.props;
+ const { loading, name } = this.state;
+ const header = translate('quality_gates.copy');
+ const submitDisabled = loading || !name || (qualityGate && qualityGate.name === name);
+
+ return (
+ <Modal contentLabel={header} onRequestClose={this.props.onClose}>
+ <form id="quality-gate-form" onSubmit={this.handleFormSubmit}>
+ <div className="modal-head">
+ <h2>{header}</h2>
+ </div>
+ <div className="modal-body">
+ <div className="modal-field">
+ <label htmlFor="quality-gate-form-name">
+ {translate('name')}
+ <em className="mandatory">*</em>
+ </label>
+ <input
+ autoFocus={true}
+ id="quality-gate-form-name"
+ maxLength={100}
+ onChange={this.handleNameChange}
+ required={true}
+ size={50}
+ type="text"
+ value={name}
+ />
+ </div>
+ </div>
+ <div className="modal-foot">
+ {loading && <i className="spinner spacer-right" />}
+ <button disabled={submitDisabled} className="js-confirm">
+ {translate('copy')}
+ </button>
+ <a href="#" className="js-modal-close" onClick={this.handleCancelClick}>
+ {translate('cancel')}
+ </a>
+ </div>
+ </form>
+ </Modal>
+ );
+ }
+}
diff --git a/server/sonar-web/src/main/js/apps/quality-gates/components/CreateQualityGateForm.tsx b/server/sonar-web/src/main/js/apps/quality-gates/components/CreateQualityGateForm.tsx
new file mode 100644
index 00000000000..1171c189e4a
--- /dev/null
+++ b/server/sonar-web/src/main/js/apps/quality-gates/components/CreateQualityGateForm.tsx
@@ -0,0 +1,127 @@
+/*
+ * 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 * as React from 'react';
+import * as PropTypes from 'prop-types';
+import Modal from '../../../components/controls/Modal';
+import { createQualityGate, QualityGate } from '../../../api/quality-gates';
+import { translate } from '../../../helpers/l10n';
+import { getQualityGateUrl } from '../../../helpers/urls';
+
+interface Props {
+ onCreate: (qualityGate: QualityGate) => void;
+ onClose: () => void;
+ organization?: string;
+}
+
+interface State {
+ loading: boolean;
+ name: string;
+}
+
+export default class CreateQualityGateForm extends React.PureComponent<Props, State> {
+ mounted: boolean;
+
+ static contextTypes = {
+ router: PropTypes.object
+ };
+
+ state = { loading: false, name: '' };
+
+ componentDidMount() {
+ this.mounted = true;
+ }
+
+ componentWillUnmount() {
+ this.mounted = false;
+ }
+
+ handleCancelClick = (event: React.SyntheticEvent<HTMLElement>) => {
+ event.preventDefault();
+ this.props.onClose();
+ };
+
+ handleNameChange = (event: React.SyntheticEvent<HTMLInputElement>) => {
+ this.setState({ name: event.currentTarget.value });
+ };
+
+ handleFormSubmit = (event: React.SyntheticEvent<HTMLFormElement>) => {
+ event.preventDefault();
+ const { organization } = this.props;
+ const { name } = this.state;
+ if (name) {
+ this.setState({ loading: true });
+ createQualityGate({ name, organization }).then(
+ qualityGate => {
+ this.props.onCreate(qualityGate);
+ this.context.router.push(getQualityGateUrl(String(qualityGate.id), organization));
+ this.props.onClose();
+ },
+ () => {
+ if (this.mounted) {
+ this.setState({ loading: false });
+ }
+ }
+ );
+ }
+ };
+
+ render() {
+ const { loading, name } = this.state;
+ const header = translate('quality_gates.rename');
+ const submitDisabled = loading || !name;
+
+ return (
+ <Modal contentLabel={header} onRequestClose={this.props.onClose}>
+ <form id="quality-gate-form" onSubmit={this.handleFormSubmit}>
+ <div className="modal-head">
+ <h2>{header}</h2>
+ </div>
+ <div className="modal-body">
+ <div className="modal-field">
+ <label htmlFor="quality-gate-form-name">
+ {translate('name')}
+ <em className="mandatory">*</em>
+ </label>
+ <input
+ autoFocus={true}
+ id="quality-gate-form-name"
+ maxLength={100}
+ onChange={this.handleNameChange}
+ required={true}
+ size={50}
+ type="text"
+ value={name}
+ />
+ </div>
+ </div>
+ <div className="modal-foot">
+ {loading && <i className="spinner spacer-right" />}
+ <button disabled={submitDisabled} className="js-confirm">
+ {translate('save')}
+ </button>
+ <a href="#" className="js-modal-close" onClick={this.handleCancelClick}>
+ {translate('cancel')}
+ </a>
+ </div>
+ </form>
+ </Modal>
+ );
+ }
+}
diff --git a/server/sonar-web/src/main/js/apps/quality-gates/components/DeleteQualityGateForm.tsx b/server/sonar-web/src/main/js/apps/quality-gates/components/DeleteQualityGateForm.tsx
new file mode 100644
index 00000000000..ab80d7e9df9
--- /dev/null
+++ b/server/sonar-web/src/main/js/apps/quality-gates/components/DeleteQualityGateForm.tsx
@@ -0,0 +1,106 @@
+import {} from '../../overview/qualityGate/QualityGate';
+/*
+ * 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 * as React from 'react';
+import * as PropTypes from 'prop-types';
+import Modal from '../../../components/controls/Modal';
+import { getQualityGatesUrl } from '../../../helpers/urls';
+import { deleteQualityGate, QualityGate } from '../../../api/quality-gates';
+import { translate, translateWithParameters } from '../../../helpers/l10n';
+
+interface Props {
+ onClose: () => void;
+ onDelete: (qualityGate: QualityGate) => void;
+ organization?: string;
+ qualityGate: QualityGate;
+}
+
+interface State {
+ loading: boolean;
+}
+
+export default class DeleteQualityGateForm extends React.PureComponent<Props, State> {
+ mounted: boolean;
+
+ static contextTypes = {
+ router: PropTypes.object
+ };
+
+ state: State = { loading: false };
+
+ componentDidMount() {
+ this.mounted = true;
+ }
+
+ componentWillUnmount() {
+ this.mounted = false;
+ }
+
+ handleCancelClick = (event: React.SyntheticEvent<HTMLElement>) => {
+ event.preventDefault();
+ this.props.onClose();
+ };
+
+ handleFormSubmit = (event: React.SyntheticEvent<HTMLFormElement>) => {
+ event.preventDefault();
+ const { organization, qualityGate } = this.props;
+ this.setState({ loading: true });
+ deleteQualityGate(qualityGate.id).then(
+ () => {
+ this.props.onDelete(qualityGate);
+ this.context.router.replace(getQualityGatesUrl(organization));
+ },
+ () => {
+ if (this.mounted) {
+ this.setState({ loading: false });
+ }
+ }
+ );
+ };
+
+ render() {
+ const { qualityGate } = this.props;
+ const header = translate('quality_gates.delete');
+
+ return (
+ <Modal contentLabel={header} onRequestClose={this.props.onClose}>
+ <form id="delete-profile-form" onSubmit={this.handleFormSubmit}>
+ <div className="modal-head">
+ <h2>{header}</h2>
+ </div>
+ <div className="modal-body">
+ <p>
+ {translateWithParameters('quality_gates.delete.confirm.message', qualityGate.name)}
+ </p>
+ </div>
+ <div className="modal-foot">
+ {this.state.loading && <i className="spinner spacer-right" />}
+ <button className="js-delete button-red" disabled={this.state.loading}>
+ {translate('delete')}
+ </button>
+ <a href="#" className="js-modal-close" onClick={this.handleCancelClick}>
+ {translate('cancel')}
+ </a>
+ </div>
+ </form>
+ </Modal>
+ );
+ }
+}
diff --git a/server/sonar-web/src/main/js/apps/quality-gates/components/Details.js b/server/sonar-web/src/main/js/apps/quality-gates/components/Details.js
index 2d3a5ad3beb..b6ff64b5c87 100644
--- a/server/sonar-web/src/main/js/apps/quality-gates/components/Details.js
+++ b/server/sonar-web/src/main/js/apps/quality-gates/components/Details.js
@@ -23,10 +23,6 @@ import Helmet from 'react-helmet';
import { fetchQualityGate, setQualityGateAsDefault } from '../../../api/quality-gates';
import DetailsHeader from './DetailsHeader';
import DetailsContent from './DetailsContent';
-import RenameView from '../views/rename-view';
-import CopyView from '../views/copy-view';
-import DeleteView from '../views/delete-view';
-import { getQualityGatesUrl, getQualityGateUrl } from '../../../helpers/urls';
export default class Details extends React.PureComponent {
static contextTypes = {
@@ -50,49 +46,8 @@ export default class Details extends React.PureComponent {
() => {}
);
- handleRenameClick = () => {
- const { qualityGate, onRename } = this.props;
- new RenameView({
- qualityGate,
- onRename: (qualityGate, newName) => {
- onRename(qualityGate, newName);
- }
- }).render();
- };
-
- handleCopyClick = () => {
- const { qualityGate, onCopy, organization } = this.props;
- const { router } = this.context;
- new CopyView({
- qualityGate,
- onCopy: newQualityGate => {
- onCopy(newQualityGate);
- router.push(getQualityGateUrl(newQualityGate.id, organization && organization.key));
- }
- }).render();
- };
-
- handleSetAsDefaultClick = () => {
- const { qualityGate, onSetAsDefault } = this.props;
- if (!qualityGate.isDefault) {
- setQualityGateAsDefault(qualityGate.id).then(() => onSetAsDefault(qualityGate), () => {});
- }
- };
-
- handleDeleteClick = () => {
- const { qualityGate, onDelete, organization } = this.props;
- const { router } = this.context;
- new DeleteView({
- qualityGate,
- onDelete: qualityGate => {
- onDelete(qualityGate);
- router.replace(getQualityGatesUrl(organization && organization.key));
- }
- }).render();
- };
-
render() {
- const { qualityGate, metrics } = this.props;
+ const { organization, metrics, qualityGate } = this.props;
const { onAddCondition, onDeleteCondition, onSaveCondition } = this.props;
if (!qualityGate) {
@@ -104,11 +59,11 @@ export default class Details extends React.PureComponent {
<Helmet title={qualityGate.name} />
<DetailsHeader
qualityGate={qualityGate}
- onRename={this.handleRenameClick}
- onCopy={this.handleCopyClick}
- onSetAsDefault={this.handleSetAsDefaultClick}
- onDelete={this.handleDeleteClick}
- organization={this.props.organization}
+ onRename={this.props.onRename}
+ onCopy={this.props.onCopy}
+ onSetAsDefault={this.props.onSetAsDefault}
+ onDelete={this.props.onDelete}
+ organization={organization && organization.key}
/>
<DetailsContent
diff --git a/server/sonar-web/src/main/js/apps/quality-gates/components/DetailsHeader.js b/server/sonar-web/src/main/js/apps/quality-gates/components/DetailsHeader.tsx
index 71ce93cf1c5..aa3d836b237 100644
--- a/server/sonar-web/src/main/js/apps/quality-gates/components/DetailsHeader.js
+++ b/server/sonar-web/src/main/js/apps/quality-gates/components/DetailsHeader.tsx
@@ -17,34 +17,59 @@
* 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 * as React from 'react';
import BuiltInQualityGateBadge from './BuiltInQualityGateBadge';
+import RenameQualityGateForm from './RenameQualityGateForm';
+import CopyQualityGateForm from './CopyQualityGateForm';
+import DeleteQualityGateForm from './DeleteQualityGateForm';
+import { QualityGate, setQualityGateAsDefault } from '../../../api/quality-gates';
import { translate } from '../../../helpers/l10n';
-export default class DetailsHeader extends React.PureComponent {
- handleRenameClick = e => {
+interface Props {
+ qualityGate: QualityGate;
+ onRename: (qualityGate: QualityGate, newName: string) => void;
+ onCopy: (newQualityGate: QualityGate) => void;
+ onSetAsDefault: (qualityGate: QualityGate) => void;
+ onDelete: (qualityGate: QualityGate) => void;
+ organization?: string;
+}
+
+interface State {
+ openPopup?: string;
+}
+
+export default class DetailsHeader extends React.PureComponent<Props, State> {
+ state = { openPopup: undefined };
+
+ handleRenameClick = (e: React.SyntheticEvent<HTMLButtonElement>) => {
e.preventDefault();
- this.props.onRename();
+ this.setState({ openPopup: 'rename' });
};
- handleCopyClick = e => {
+ handleCopyClick = (e: React.SyntheticEvent<HTMLButtonElement>) => {
e.preventDefault();
- this.props.onCopy();
+ this.setState({ openPopup: 'copy' });
};
- handleSetAsDefaultClick = e => {
+ handleSetAsDefaultClick = (e: React.SyntheticEvent<HTMLButtonElement>) => {
e.preventDefault();
- this.props.onSetAsDefault();
+ const { qualityGate, onSetAsDefault } = this.props;
+ if (!qualityGate.isDefault) {
+ setQualityGateAsDefault(qualityGate.id).then(() => onSetAsDefault(qualityGate), () => {});
+ }
};
- handleDeleteClick = e => {
+ handleDeleteClick = (e: React.SyntheticEvent<HTMLButtonElement>) => {
e.preventDefault();
- this.props.onDelete();
+ this.setState({ openPopup: 'delete' });
};
+ handleClosePopup = () => this.setState({ openPopup: undefined });
+
render() {
- const { qualityGate } = this.props;
- const actions = qualityGate.actions || {};
+ const { organization, qualityGate } = this.props;
+ const { openPopup } = this.state;
+ const actions = qualityGate.actions || ({} as any);
return (
<div className="layout-page-header-panel layout-page-main-header issues-main-header">
<div className="layout-page-header-panel-inner layout-page-main-header-inner">
@@ -84,6 +109,32 @@ export default class DetailsHeader extends React.PureComponent {
{translate('delete')}
</button>
)}
+ {openPopup === 'rename' && (
+ <RenameQualityGateForm
+ onRename={this.props.onRename}
+ organization={organization}
+ onClose={this.handleClosePopup}
+ qualityGate={qualityGate}
+ />
+ )}
+
+ {openPopup === 'copy' && (
+ <CopyQualityGateForm
+ onCopy={this.props.onCopy}
+ organization={organization}
+ onClose={this.handleClosePopup}
+ qualityGate={qualityGate}
+ />
+ )}
+
+ {openPopup === 'delete' && (
+ <DeleteQualityGateForm
+ onDelete={this.props.onDelete}
+ organization={organization}
+ onClose={this.handleClosePopup}
+ qualityGate={qualityGate}
+ />
+ )}
</div>
</div>
</div>
diff --git a/server/sonar-web/src/main/js/apps/quality-gates/components/List.js b/server/sonar-web/src/main/js/apps/quality-gates/components/List.js
index d29633c5102..bc22c41a817 100644
--- a/server/sonar-web/src/main/js/apps/quality-gates/components/List.js
+++ b/server/sonar-web/src/main/js/apps/quality-gates/components/List.js
@@ -29,7 +29,7 @@ export default function List({ organization, qualityGates }) {
{qualityGates.map(qualityGate => (
<Link
key={qualityGate.id}
- to={getQualityGateUrl(qualityGate.id, organization && organization.key)}
+ to={getQualityGateUrl(String(qualityGate.id), organization && organization.key)}
activeClassName="active"
className="list-group-item"
data-id={qualityGate.id}>
diff --git a/server/sonar-web/src/main/js/apps/quality-gates/components/ListHeader.js b/server/sonar-web/src/main/js/apps/quality-gates/components/ListHeader.js
deleted file mode 100644
index 2801cd954bb..00000000000
--- a/server/sonar-web/src/main/js/apps/quality-gates/components/ListHeader.js
+++ /dev/null
@@ -1,42 +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 React from 'react';
-import CreateView from '../views/create-view';
-import { translate } from '../../../helpers/l10n';
-
-export default function ListHeader({ canCreate, onAdd }) {
- function handleAddClick(e) {
- e.preventDefault();
- new CreateView({ onAdd }).render();
- }
-
- return (
- <header className="page-header">
- <h1 className="page-title">{translate('quality_gates.page')}</h1>
- {canCreate && (
- <div className="page-actions">
- <button id="quality-gate-add" onClick={handleAddClick}>
- {translate('create')}
- </button>
- </div>
- )}
- </header>
- );
-}
diff --git a/server/sonar-web/src/main/js/apps/quality-gates/components/ListHeader.tsx b/server/sonar-web/src/main/js/apps/quality-gates/components/ListHeader.tsx
new file mode 100644
index 00000000000..d9ec9060188
--- /dev/null
+++ b/server/sonar-web/src/main/js/apps/quality-gates/components/ListHeader.tsx
@@ -0,0 +1,65 @@
+/*
+ * 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 * as React from 'react';
+import CreateQualityGateForm from '../components/CreateQualityGateForm';
+import { QualityGate } from '../../../api/quality-gates';
+import { translate } from '../../../helpers/l10n';
+import { Organization } from '../../../app/types';
+
+interface Props {
+ canCreate: boolean;
+ onAdd: (qualityGate: QualityGate) => void;
+ organization?: Organization;
+}
+
+interface State {
+ createQualityGateOpen: boolean;
+}
+
+export default class ListHeader extends React.PureComponent<Props, State> {
+ state = { createQualityGateOpen: false };
+
+ openCreateQualityGateForm = () => this.setState({ createQualityGateOpen: true });
+ closeCreateQualityGateForm = () => this.setState({ createQualityGateOpen: false });
+
+ render() {
+ const { organization } = this.props;
+
+ return (
+ <header className="page-header">
+ <h1 className="page-title">{translate('quality_gates.page')}</h1>
+ {this.props.canCreate && (
+ <div className="page-actions">
+ <button id="quality-gate-add" onClick={this.openCreateQualityGateForm}>
+ {translate('create')}
+ </button>
+ </div>
+ )}
+ {this.state.createQualityGateOpen && (
+ <CreateQualityGateForm
+ onClose={this.closeCreateQualityGateForm}
+ onCreate={this.props.onAdd}
+ organization={organization && organization.key}
+ />
+ )}
+ </header>
+ );
+ }
+}
diff --git a/server/sonar-web/src/main/js/apps/quality-gates/components/QualityGatesApp.js b/server/sonar-web/src/main/js/apps/quality-gates/components/QualityGatesApp.js
index 8d13fbae35e..96c3ed76f9a 100644
--- a/server/sonar-web/src/main/js/apps/quality-gates/components/QualityGatesApp.js
+++ b/server/sonar-web/src/main/js/apps/quality-gates/components/QualityGatesApp.js
@@ -56,19 +56,11 @@ export default class QualityGatesApp extends Component {
updateStore({ actions, qualityGates });
if (qualityGates && qualityGates.length === 1 && !actions.create) {
this.context.router.replace(
- getQualityGateUrl(qualityGates[0].id, organization && organization.key)
+ getQualityGateUrl(String(qualityGates[0].id), organization && organization.key)
);
}
});
- handleAdd = qualityGate => {
- const { addQualityGate, organization } = this.props;
- const { router } = this.context;
-
- addQualityGate(qualityGate);
- router.push(getQualityGateUrl(qualityGate.id, organization && organization.key));
- };
-
render() {
const { children, qualityGates, actions, organization } = this.props;
const defaultTitle = translate('quality_gates.page');
@@ -81,7 +73,11 @@ export default class QualityGatesApp extends Component {
<div className="layout-page-side" style={{ top }}>
<div className="layout-page-side-inner">
<div className="layout-page-filters">
- <ListHeader canCreate={actions && actions.create} onAdd={this.handleAdd} />
+ <ListHeader
+ canCreate={actions && actions.create}
+ onAdd={this.props.addQualityGate}
+ organization={organization}
+ />
{qualityGates && <List organization={organization} qualityGates={qualityGates} />}
</div>
</div>
diff --git a/server/sonar-web/src/main/js/apps/quality-gates/components/RenameQualityGateForm.tsx b/server/sonar-web/src/main/js/apps/quality-gates/components/RenameQualityGateForm.tsx
new file mode 100644
index 00000000000..b4a453fb678
--- /dev/null
+++ b/server/sonar-web/src/main/js/apps/quality-gates/components/RenameQualityGateForm.tsx
@@ -0,0 +1,125 @@
+/*
+ * 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 * as React from 'react';
+import Modal from '../../../components/controls/Modal';
+import { QualityGate, renameQualityGate } from '../../../api/quality-gates';
+import { translate } from '../../../helpers/l10n';
+
+interface Props {
+ qualityGate: QualityGate;
+ onRename: (qualityGate: QualityGate, newName: string) => void;
+ onClose: () => void;
+ organization?: string;
+}
+
+interface State {
+ loading: boolean;
+ name: string;
+}
+
+export default class RenameQualityGateForm extends React.PureComponent<Props, State> {
+ mounted: boolean;
+
+ constructor(props: Props) {
+ super(props);
+ this.state = { loading: false, name: props.qualityGate.name };
+ }
+
+ componentDidMount() {
+ this.mounted = true;
+ }
+
+ componentWillUnmount() {
+ this.mounted = false;
+ }
+
+ handleCancelClick = (event: React.SyntheticEvent<HTMLElement>) => {
+ event.preventDefault();
+ this.props.onClose();
+ };
+
+ handleNameChange = (event: React.SyntheticEvent<HTMLInputElement>) => {
+ this.setState({ name: event.currentTarget.value });
+ };
+
+ handleFormSubmit = (event: React.SyntheticEvent<HTMLFormElement>) => {
+ event.preventDefault();
+ const { qualityGate, organization } = this.props;
+ const { name } = this.state;
+ if (name) {
+ this.setState({ loading: true });
+ renameQualityGate({ id: qualityGate.id, name, organization }).then(
+ () => {
+ this.props.onRename(qualityGate, name);
+ this.props.onClose();
+ },
+ () => {
+ if (this.mounted) {
+ this.setState({ loading: false });
+ }
+ }
+ );
+ }
+ };
+
+ render() {
+ const { qualityGate } = this.props;
+ const { loading, name } = this.state;
+ const header = translate('quality_gates.rename');
+ const submitDisabled = loading || !name || (qualityGate && qualityGate.name === name);
+
+ return (
+ <Modal contentLabel={header} onRequestClose={this.props.onClose}>
+ <form id="quality-gate-form" onSubmit={this.handleFormSubmit}>
+ <div className="modal-head">
+ <h2>{header}</h2>
+ </div>
+ <div className="modal-body">
+ <div className="modal-field">
+ <label htmlFor="quality-gate-form-name">
+ {translate('name')}
+ <em className="mandatory">*</em>
+ </label>
+ <input
+ autoFocus={true}
+ id="quality-gate-form-name"
+ maxLength={100}
+ onChange={this.handleNameChange}
+ required={true}
+ size={50}
+ type="text"
+ value={name}
+ />
+ </div>
+ </div>
+ <div className="modal-foot">
+ {loading && <i className="spinner spacer-right" />}
+ <button disabled={submitDisabled} className="js-confirm">
+ {translate('rename')}
+ </button>
+ <a href="#" className="js-modal-close" onClick={this.handleCancelClick}>
+ {translate('cancel')}
+ </a>
+ </div>
+ </form>
+ </Modal>
+ );
+ }
+}
diff --git a/server/sonar-web/src/main/js/apps/quality-gates/templates/quality-gate-form.hbs b/server/sonar-web/src/main/js/apps/quality-gates/templates/quality-gate-form.hbs
deleted file mode 100644
index bc6e623018e..00000000000
--- a/server/sonar-web/src/main/js/apps/quality-gates/templates/quality-gate-form.hbs
+++ /dev/null
@@ -1,20 +0,0 @@
-<form id="quality-gate-form">
- <div class="modal-head">
- <h2>{{t 'quality_gates' method }}</h2>
- </div>
- <div class="modal-body">
- <div class="js-modal-messages"></div>
- <div class="modal-field">
- <label for="quality-gate-form-name">{{t 'name'}} <em class="mandatory">*</em></label>
- <input id="quality-gate-form-name" type="text" size="50" maxlength="100" value="{{name}}" required>
- </div>
- </div>
- <div class="modal-foot">
- <button id="quality-gate-form-submit">
- {{#eq method 'rename'}}{{t 'save'}}{{/eq}}
- {{#eq method 'copy'}}{{t 'copy'}}{{/eq}}
- {{#eq method 'create'}}{{t 'create'}}{{/eq}}
- </button>
- <a id="quality-gate-form-cancel" href="#" class="js-modal-close">{{t 'cancel'}}</a>
- </div>
-</form>
diff --git a/server/sonar-web/src/main/js/apps/quality-gates/templates/quality-gates-delete.hbs b/server/sonar-web/src/main/js/apps/quality-gates/templates/quality-gates-delete.hbs
deleted file mode 100644
index c146c581bae..00000000000
--- a/server/sonar-web/src/main/js/apps/quality-gates/templates/quality-gates-delete.hbs
+++ /dev/null
@@ -1,17 +0,0 @@
-<form id="delete-gate-form">
- <div class="modal-head">
- <h2>{{t 'quality_gates.delete'}}</h2>
- </div>
- <div class="modal-body">
- <div class="js-modal-messages"></div>
- {{#if isDefault}}
- {{tp 'quality_gates.delete.confirm.default' name}}
- {{else}}
- {{tp 'quality_gates.delete.confirm.message' name}}
- {{/if}}
- </div>
- <div class="modal-foot">
- <button id="delete-gate-submit">{{t 'delete'}}</button>
- <a href="#" class="js-modal-close" id="delete-gate-cancel">{{t 'cancel'}}</a>
- </div>
-</form>
diff --git a/server/sonar-web/src/main/js/apps/quality-gates/views/copy-view.js b/server/sonar-web/src/main/js/apps/quality-gates/views/copy-view.js
deleted file mode 100644
index a9b6da0d2f2..00000000000
--- a/server/sonar-web/src/main/js/apps/quality-gates/views/copy-view.js
+++ /dev/null
@@ -1,53 +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 '../templates/quality-gate-form.hbs';
-import { copyQualityGate } from '../../../api/quality-gates';
-import { parseError } from '../../../helpers/request';
-
-export default ModalForm.extend({
- template: Template,
-
- onFormSubmit() {
- ModalForm.prototype.onFormSubmit.apply(this, arguments);
- this.disableForm();
- this.sendRequest();
- },
-
- sendRequest() {
- const { id } = this.options.qualityGate;
- const name = this.$('#quality-gate-form-name').val();
-
- copyQualityGate(id, name).then(
- qualityGate => {
- this.destroy();
- this.options.onCopy(qualityGate);
- },
- error => {
- this.enableForm();
- parseError(error).then(msg => this.showErrors([{ msg }]));
- }
- );
- },
-
- serializeData() {
- return { method: 'copy', ...this.options.qualityGate };
- }
-});
diff --git a/server/sonar-web/src/main/js/apps/quality-gates/views/create-view.js b/server/sonar-web/src/main/js/apps/quality-gates/views/create-view.js
deleted file mode 100644
index 3580b45fcf6..00000000000
--- a/server/sonar-web/src/main/js/apps/quality-gates/views/create-view.js
+++ /dev/null
@@ -1,52 +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 '../templates/quality-gate-form.hbs';
-import { createQualityGate } from '../../../api/quality-gates';
-import { parseError } from '../../../helpers/request';
-
-export default ModalForm.extend({
- template: Template,
-
- onFormSubmit() {
- ModalForm.prototype.onFormSubmit.apply(this, arguments);
- this.disableForm();
- this.sendRequest();
- },
-
- sendRequest() {
- const name = this.$('#quality-gate-form-name').val();
-
- createQualityGate(name).then(
- qualityGate => {
- this.destroy();
- this.options.onAdd(qualityGate);
- },
- error => {
- this.enableForm();
- parseError(error).then(msg => this.showErrors([{ msg }]));
- }
- );
- },
-
- serializeData() {
- return { method: 'create' };
- }
-});
diff --git a/server/sonar-web/src/main/js/apps/quality-gates/views/delete-view.js b/server/sonar-web/src/main/js/apps/quality-gates/views/delete-view.js
deleted file mode 100644
index 3327a6d3a10..00000000000
--- a/server/sonar-web/src/main/js/apps/quality-gates/views/delete-view.js
+++ /dev/null
@@ -1,52 +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 '../templates/quality-gates-delete.hbs';
-import { deleteQualityGate } from '../../../api/quality-gates';
-import { parseError } from '../../../helpers/request';
-
-export default ModalForm.extend({
- template: Template,
-
- onFormSubmit() {
- ModalForm.prototype.onFormSubmit.apply(this, arguments);
- this.disableForm();
- this.sendRequest();
- },
-
- sendRequest() {
- const { id } = this.options.qualityGate;
-
- deleteQualityGate(id).then(
- () => {
- this.destroy();
- this.options.onDelete(this.options.qualityGate);
- },
- error => {
- this.enableForm();
- parseError(error).then(msg => this.showErrors([{ msg }]));
- }
- );
- },
-
- serializeData() {
- return this.options.qualityGate;
- }
-});
diff --git a/server/sonar-web/src/main/js/apps/quality-gates/views/gate-projects-view.js b/server/sonar-web/src/main/js/apps/quality-gates/views/gate-projects-view.js
index 7909394dfe4..1b54745a857 100644
--- a/server/sonar-web/src/main/js/apps/quality-gates/views/gate-projects-view.js
+++ b/server/sonar-web/src/main/js/apps/quality-gates/views/gate-projects-view.js
@@ -25,9 +25,20 @@ import { translate } from '../../../helpers/l10n';
export default Marionette.ItemView.extend({
template: () => {},
+ initialize(options) {
+ this.organization = options.organization;
+ },
+
onRender() {
const { qualityGate } = this.options;
+ const extra = {
+ gateId: qualityGate.id
+ };
+ if (this.organization) {
+ extra.organization = this.organization.key;
+ }
+
new SelectList({
el: this.options.container,
width: '100%',
@@ -39,9 +50,7 @@ export default Marionette.ItemView.extend({
searchUrl: window.baseUrl + '/api/qualitygates/search?gateId=' + qualityGate.id,
selectUrl: window.baseUrl + '/api/qualitygates/select',
deselectUrl: window.baseUrl + '/api/qualitygates/deselect',
- extra: {
- gateId: qualityGate.id
- },
+ extra,
selectParameter: 'projectId',
selectParameterValue: 'id',
labels: {
diff --git a/server/sonar-web/src/main/js/apps/quality-gates/views/rename-view.js b/server/sonar-web/src/main/js/apps/quality-gates/views/rename-view.js
deleted file mode 100644
index 2614ea6af67..00000000000
--- a/server/sonar-web/src/main/js/apps/quality-gates/views/rename-view.js
+++ /dev/null
@@ -1,53 +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 '../templates/quality-gate-form.hbs';
-import { renameQualityGate } from '../../../api/quality-gates';
-import { parseError } from '../../../helpers/request';
-
-export default ModalForm.extend({
- template: Template,
-
- onFormSubmit() {
- ModalForm.prototype.onFormSubmit.apply(this, arguments);
- this.disableForm();
- this.sendRequest();
- },
-
- sendRequest() {
- const { id } = this.options.qualityGate;
- const name = this.$('#quality-gate-form-name').val();
-
- renameQualityGate(id, name).then(
- () => {
- this.destroy();
- this.options.onRename(this.options.qualityGate, name);
- },
- error => {
- this.enableForm();
- parseError(error).then(msg => this.showErrors([{ msg }]));
- }
- );
- },
-
- serializeData() {
- return { method: 'rename', ...this.options.qualityGate };
- }
-});