]> source.dussan.org Git - sonarqube.git/commitdiff
simplify routes
authorStas Vilchik <stas.vilchik@sonarsource.com>
Mon, 10 Dec 2018 10:52:42 +0000 (11:52 +0100)
committerSonarTech <sonartech@sonarsource.com>
Tue, 8 Jan 2019 19:21:05 +0000 (20:21 +0100)
server/sonar-web/src/main/js/api/quality-gates.ts
server/sonar-web/src/main/js/apps/quality-gates/components/App.tsx [new file with mode: 0644]
server/sonar-web/src/main/js/apps/quality-gates/components/Details.tsx [new file with mode: 0644]
server/sonar-web/src/main/js/apps/quality-gates/components/DetailsApp.tsx [deleted file]
server/sonar-web/src/main/js/apps/quality-gates/components/QualityGatesApp.tsx [deleted file]
server/sonar-web/src/main/js/apps/quality-gates/routes.ts

index 7c1246f61743f6a2662a66bc30ee2c73165c7662..b1568744d4b05e66dabc77994f0cfcb4d3ec7379 100644 (file)
@@ -30,7 +30,7 @@ export function fetchQualityGates(data: {
 }
 
 export function fetchQualityGate(data: {
-  id: number;
+  id: number | string;
   organization?: string;
 }): Promise<T.QualityGate> {
   return getJSON('/api/qualitygates/show', data).catch(throwGlobalError);
diff --git a/server/sonar-web/src/main/js/apps/quality-gates/components/App.tsx b/server/sonar-web/src/main/js/apps/quality-gates/components/App.tsx
new file mode 100644 (file)
index 0000000..a1ad8e3
--- /dev/null
@@ -0,0 +1,165 @@
+/*
+ * SonarQube
+ * Copyright (C) 2009-2019 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 { WithRouterProps } from 'react-router';
+import Helmet from 'react-helmet';
+import Details from './Details';
+import Intro from './Intro';
+import List from './List';
+import ListHeader from './ListHeader';
+import DeferredSpinner from '../../../components/common/DeferredSpinner';
+import ScreenPositionHelper from '../../../components/common/ScreenPositionHelper';
+import Suggestions from '../../../app/components/embed-docs-modal/Suggestions';
+import { fetchQualityGates } from '../../../api/quality-gates';
+import { translate } from '../../../helpers/l10n';
+import {
+  addSideBarClass,
+  addWhitePageClass,
+  removeSideBarClass,
+  removeWhitePageClass
+} from '../../../helpers/pages';
+import { getQualityGateUrl } from '../../../helpers/urls';
+import '../../../components/search-navigator.css';
+import '../styles.css';
+
+interface Props extends WithRouterProps {
+  organization?: Pick<T.Organization, 'key'>;
+}
+
+interface State {
+  canCreate: boolean;
+  loading: boolean;
+  qualityGates: T.QualityGate[];
+}
+
+class App extends React.PureComponent<Props, State> {
+  mounted = false;
+  state: State = { canCreate: false, loading: true, qualityGates: [] };
+
+  componentDidMount() {
+    this.mounted = true;
+    this.fetchQualityGates();
+    addWhitePageClass();
+    addSideBarClass();
+  }
+
+  componentWillUnmount() {
+    this.mounted = false;
+    removeWhitePageClass();
+    removeSideBarClass();
+  }
+
+  fetchQualityGates = () => {
+    const { organization } = this.props;
+    return fetchQualityGates({ organization: organization && organization.key }).then(
+      ({ actions, qualitygates: qualityGates }) => {
+        if (this.mounted) {
+          this.setState({ canCreate: actions.create, loading: false, qualityGates });
+
+          if (qualityGates && qualityGates.length === 1 && !actions.create) {
+            this.props.router.replace(
+              getQualityGateUrl(String(qualityGates[0].id), organization && organization.key)
+            );
+          }
+        }
+      },
+      () => {
+        if (this.mounted) {
+          this.setState({ loading: false });
+        }
+      }
+    );
+  };
+
+  handleSetDefault = (qualityGate: T.QualityGate) => {
+    this.setState(({ qualityGates }) => {
+      return {
+        qualityGates: qualityGates.map(candidate => {
+          if (candidate.isDefault || candidate.id === qualityGate.id) {
+            return { ...candidate, isDefault: candidate.id === qualityGate.id };
+          }
+          return candidate;
+        })
+      };
+    });
+  };
+
+  renderContent() {
+    const { id } = this.props.params;
+    const organizationKey = this.props.organization && this.props.organization.key;
+    if (id !== undefined) {
+      return (
+        <Details
+          id={id}
+          onSetDefault={this.handleSetDefault}
+          organization={organizationKey}
+          qualityGates={this.state.qualityGates}
+          refreshQualityGates={this.fetchQualityGates}
+        />
+      );
+    } else {
+      return (
+        <Intro
+          organization={organizationKey}
+          qualityGates={this.state.qualityGates}
+          router={this.props.router}
+        />
+      );
+    }
+  }
+
+  render() {
+    const { canCreate, qualityGates } = this.state;
+    const defaultTitle = translate('quality_gates.page');
+    const organization = this.props.organization && this.props.organization.key;
+
+    return (
+      <>
+        <Helmet defaultTitle={defaultTitle} titleTemplate={'%s - ' + defaultTitle} />
+        <div className="layout-page" id="quality-gates-page">
+          <Suggestions suggestions="quality_gates" />
+
+          <ScreenPositionHelper className="layout-page-side-outer">
+            {({ top }) => (
+              <div className="layout-page-side" style={{ top }}>
+                <div className="layout-page-side-inner">
+                  <div className="layout-page-filters">
+                    <ListHeader
+                      canCreate={canCreate}
+                      organization={organization}
+                      refreshQualityGates={this.fetchQualityGates}
+                    />
+                    {qualityGates.length > 0 && (
+                      <List organization={organization} qualityGates={qualityGates} />
+                    )}
+                  </div>
+                </div>
+              </div>
+            )}
+          </ScreenPositionHelper>
+
+          <DeferredSpinner loading={this.state.loading}>{this.renderContent()}</DeferredSpinner>
+        </div>
+      </>
+    );
+  }
+}
+
+export default App;
diff --git a/server/sonar-web/src/main/js/apps/quality-gates/components/Details.tsx b/server/sonar-web/src/main/js/apps/quality-gates/components/Details.tsx
new file mode 100644 (file)
index 0000000..cd58b7f
--- /dev/null
@@ -0,0 +1,174 @@
+/*
+ * SonarQube
+ * Copyright (C) 2009-2019 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 Helmet from 'react-helmet';
+import { connect } from 'react-redux';
+import DetailsHeader from './DetailsHeader';
+import DetailsContent from './DetailsContent';
+import { getMetrics, Store } from '../../../store/rootReducer';
+import { fetchMetrics } from '../../../store/rootActions';
+import { fetchQualityGate } from '../../../api/quality-gates';
+import { checkIfDefault, addCondition, replaceCondition, deleteCondition } from '../utils';
+
+interface OwnProps {
+  id: string;
+  onSetDefault: (qualityGate: T.QualityGate) => void;
+  organization?: string;
+  qualityGates: T.QualityGate[];
+  refreshQualityGates: () => Promise<void>;
+}
+
+interface StateToProps {
+  metrics: { [key: string]: T.Metric };
+}
+
+interface DispatchToProps {
+  fetchMetrics: () => void;
+}
+
+type Props = StateToProps & DispatchToProps & OwnProps;
+
+interface State {
+  loading: boolean;
+  qualityGate?: T.QualityGate;
+}
+
+export class Details extends React.PureComponent<Props, State> {
+  mounted = false;
+  state: State = { loading: true };
+
+  componentDidMount() {
+    this.mounted = true;
+    this.props.fetchMetrics();
+    this.fetchDetails();
+  }
+
+  componentDidUpdate(prevProps: Props) {
+    if (prevProps.id !== this.props.id) {
+      this.fetchDetails();
+    }
+  }
+
+  componentWillUnmount() {
+    this.mounted = false;
+  }
+
+  fetchDetails = () => {
+    const { id, organization } = this.props;
+    this.setState({ loading: true });
+    return fetchQualityGate({ id, organization }).then(
+      qualityGate => {
+        if (this.mounted) {
+          this.setState({ loading: false, qualityGate });
+        }
+      },
+      () => {
+        if (this.mounted) {
+          this.setState({ loading: false });
+        }
+      }
+    );
+  };
+
+  handleAddCondition = (condition: T.Condition) => {
+    this.setState(({ qualityGate }) => {
+      if (!qualityGate) {
+        return null;
+      }
+      return { qualityGate: addCondition(qualityGate, condition) };
+    });
+  };
+
+  handleSaveCondition = (newCondition: T.Condition, oldCondition: T.Condition) => {
+    this.setState(({ qualityGate }) => {
+      if (!qualityGate) {
+        return null;
+      }
+      return { qualityGate: replaceCondition(qualityGate, newCondition, oldCondition) };
+    });
+  };
+
+  handleRemoveCondition = (condition: T.Condition) => {
+    this.setState(({ qualityGate }) => {
+      if (!qualityGate) {
+        return null;
+      }
+      return { qualityGate: deleteCondition(qualityGate, condition) };
+    });
+  };
+
+  handleSetDefault = () => {
+    this.setState(({ qualityGate }) => {
+      if (!qualityGate) {
+        return null;
+      }
+      this.props.onSetDefault(qualityGate);
+      const newQualityGate: T.QualityGate = {
+        ...qualityGate,
+        actions: { ...qualityGate.actions, delete: false, setAsDefault: false }
+      };
+      return { qualityGate: newQualityGate };
+    });
+  };
+
+  render() {
+    const { organization, metrics, refreshQualityGates } = this.props;
+    const { qualityGate } = this.state;
+
+    if (!qualityGate) {
+      return null;
+    }
+
+    return (
+      <>
+        <Helmet title={qualityGate.name} />
+        <div className="layout-page-main">
+          <DetailsHeader
+            onSetDefault={this.handleSetDefault}
+            organization={organization}
+            qualityGate={qualityGate}
+            refreshItem={this.fetchDetails}
+            refreshList={refreshQualityGates}
+          />
+          <DetailsContent
+            isDefault={checkIfDefault(qualityGate, this.props.qualityGates)}
+            metrics={metrics}
+            onAddCondition={this.handleAddCondition}
+            onRemoveCondition={this.handleRemoveCondition}
+            onSaveCondition={this.handleSaveCondition}
+            organization={organization}
+            qualityGate={qualityGate}
+          />
+        </div>
+      </>
+    );
+  }
+}
+
+const mapDispatchToProps: DispatchToProps = { fetchMetrics };
+
+const mapStateToProps = (state: Store): StateToProps => ({
+  metrics: getMetrics(state)
+});
+
+export default connect(
+  mapStateToProps,
+  mapDispatchToProps
+)(Details);
diff --git a/server/sonar-web/src/main/js/apps/quality-gates/components/DetailsApp.tsx b/server/sonar-web/src/main/js/apps/quality-gates/components/DetailsApp.tsx
deleted file mode 100644 (file)
index 56104f2..0000000
+++ /dev/null
@@ -1,176 +0,0 @@
-/*
- * SonarQube
- * Copyright (C) 2009-2019 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 { withRouter, WithRouterProps } from 'react-router';
-import Helmet from 'react-helmet';
-import { connect } from 'react-redux';
-import DetailsHeader from './DetailsHeader';
-import DetailsContent from './DetailsContent';
-import { getMetrics, Store } from '../../../store/rootReducer';
-import { fetchMetrics } from '../../../store/rootActions';
-import { fetchQualityGate } from '../../../api/quality-gates';
-import { checkIfDefault, addCondition, replaceCondition, deleteCondition } from '../utils';
-
-interface OwnProps {
-  onSetDefault: (qualityGate: T.QualityGate) => void;
-  organization?: string;
-  params: { id: number };
-  qualityGates: T.QualityGate[];
-  refreshQualityGates: () => Promise<void>;
-}
-
-interface StateToProps {
-  metrics: { [key: string]: T.Metric };
-}
-
-interface DispatchToProps {
-  fetchMetrics: () => void;
-}
-
-type Props = StateToProps & DispatchToProps & OwnProps & WithRouterProps;
-
-interface State {
-  loading: boolean;
-  qualityGate?: T.QualityGate;
-}
-
-export class DetailsApp extends React.PureComponent<Props, State> {
-  mounted = false;
-  state: State = { loading: true };
-
-  componentDidMount() {
-    this.mounted = true;
-    this.props.fetchMetrics();
-    this.fetchDetails();
-  }
-
-  componentWillReceiveProps(nextProps: Props) {
-    if (nextProps.params.id !== this.props.params.id) {
-      this.setState({ loading: true });
-      this.fetchDetails(nextProps);
-    }
-  }
-
-  componentWillUnmount() {
-    this.mounted = false;
-  }
-
-  fetchDetails = ({ organization, params } = this.props) => {
-    return fetchQualityGate({ id: params.id, organization }).then(
-      qualityGate => {
-        if (this.mounted) {
-          this.setState({ loading: false, qualityGate });
-        }
-      },
-      () => {
-        if (this.mounted) {
-          this.setState({ loading: false });
-        }
-      }
-    );
-  };
-
-  handleAddCondition = (condition: T.Condition) => {
-    this.setState(({ qualityGate }) => {
-      if (!qualityGate) {
-        return null;
-      }
-      return { qualityGate: addCondition(qualityGate, condition) };
-    });
-  };
-
-  handleSaveCondition = (newCondition: T.Condition, oldCondition: T.Condition) => {
-    this.setState(({ qualityGate }) => {
-      if (!qualityGate) {
-        return null;
-      }
-      return { qualityGate: replaceCondition(qualityGate, newCondition, oldCondition) };
-    });
-  };
-
-  handleRemoveCondition = (condition: T.Condition) => {
-    this.setState(({ qualityGate }) => {
-      if (!qualityGate) {
-        return null;
-      }
-      return { qualityGate: deleteCondition(qualityGate, condition) };
-    });
-  };
-
-  handleSetDefault = () => {
-    this.setState(({ qualityGate }) => {
-      if (!qualityGate) {
-        return null;
-      }
-      this.props.onSetDefault(qualityGate);
-      const newQualityGate: T.QualityGate = {
-        ...qualityGate,
-        actions: { ...qualityGate.actions, delete: false, setAsDefault: false }
-      };
-      return { qualityGate: newQualityGate };
-    });
-  };
-
-  render() {
-    const { organization, metrics, refreshQualityGates } = this.props;
-    const { qualityGate } = this.state;
-
-    if (!qualityGate) {
-      return null;
-    }
-
-    return (
-      <>
-        <Helmet title={qualityGate.name} />
-        <div className="layout-page-main">
-          <DetailsHeader
-            onSetDefault={this.handleSetDefault}
-            organization={organization}
-            qualityGate={qualityGate}
-            refreshItem={this.fetchDetails}
-            refreshList={refreshQualityGates}
-          />
-          <DetailsContent
-            isDefault={checkIfDefault(qualityGate, this.props.qualityGates)}
-            metrics={metrics}
-            onAddCondition={this.handleAddCondition}
-            onRemoveCondition={this.handleRemoveCondition}
-            onSaveCondition={this.handleSaveCondition}
-            organization={organization}
-            qualityGate={qualityGate}
-          />
-        </div>
-      </>
-    );
-  }
-}
-
-const mapDispatchToProps: DispatchToProps = { fetchMetrics };
-
-const mapStateToProps = (state: Store): StateToProps => ({
-  metrics: getMetrics(state)
-});
-
-export default withRouter(
-  connect(
-    mapStateToProps,
-    mapDispatchToProps
-  )(DetailsApp)
-);
diff --git a/server/sonar-web/src/main/js/apps/quality-gates/components/QualityGatesApp.tsx b/server/sonar-web/src/main/js/apps/quality-gates/components/QualityGatesApp.tsx
deleted file mode 100644 (file)
index 716c349..0000000
+++ /dev/null
@@ -1,148 +0,0 @@
-/*
- * SonarQube
- * Copyright (C) 2009-2019 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 { withRouter, WithRouterProps } from 'react-router';
-import Helmet from 'react-helmet';
-import ListHeader from './ListHeader';
-import List from './List';
-import ScreenPositionHelper from '../../../components/common/ScreenPositionHelper';
-import Suggestions from '../../../app/components/embed-docs-modal/Suggestions';
-import { fetchQualityGates } from '../../../api/quality-gates';
-import { translate } from '../../../helpers/l10n';
-import {
-  addSideBarClass,
-  addWhitePageClass,
-  removeSideBarClass,
-  removeWhitePageClass
-} from '../../../helpers/pages';
-import { getQualityGateUrl } from '../../../helpers/urls';
-import '../../../components/search-navigator.css';
-import '../styles.css';
-
-interface Props extends WithRouterProps {
-  children: React.ReactElement<{
-    organization?: string;
-    refreshQualityGates: () => Promise<void>;
-  }>;
-  organization: Pick<T.Organization, 'key'>;
-}
-
-interface State {
-  canCreate: boolean;
-  loading: boolean;
-  qualityGates: T.QualityGate[];
-}
-
-class QualityGatesApp extends React.PureComponent<Props, State> {
-  mounted = false;
-  state: State = { canCreate: false, loading: true, qualityGates: [] };
-
-  componentDidMount() {
-    this.mounted = true;
-    this.fetchQualityGates();
-    addWhitePageClass();
-    addSideBarClass();
-  }
-
-  componentWillUnmount() {
-    this.mounted = false;
-    removeWhitePageClass();
-    removeSideBarClass();
-  }
-
-  fetchQualityGates = () => {
-    const { organization } = this.props;
-    return fetchQualityGates({ organization: organization && organization.key }).then(
-      ({ actions, qualitygates: qualityGates }) => {
-        if (this.mounted) {
-          this.setState({ canCreate: actions.create, loading: false, qualityGates });
-
-          if (qualityGates && qualityGates.length === 1 && !actions.create) {
-            this.props.router.replace(
-              getQualityGateUrl(String(qualityGates[0].id), organization && organization.key)
-            );
-          }
-        }
-      },
-      () => {
-        if (this.mounted) {
-          this.setState({ loading: false });
-        }
-      }
-    );
-  };
-
-  handleSetDefault = (qualityGate: T.QualityGate) => {
-    this.setState(({ qualityGates }) => {
-      return {
-        qualityGates: qualityGates.map(candidate => {
-          if (candidate.isDefault || candidate.id === qualityGate.id) {
-            return { ...candidate, isDefault: candidate.id === qualityGate.id };
-          }
-          return candidate;
-        })
-      };
-    });
-  };
-
-  render() {
-    const { children } = this.props;
-    const { canCreate, loading, qualityGates } = this.state;
-    const defaultTitle = translate('quality_gates.page');
-    const organization = this.props.organization && this.props.organization.key;
-
-    return (
-      <>
-        <Helmet defaultTitle={defaultTitle} titleTemplate={'%s - ' + defaultTitle} />
-        <div className="layout-page" id="quality-gates-page">
-          <Suggestions suggestions="quality_gates" />
-
-          <ScreenPositionHelper className="layout-page-side-outer">
-            {({ top }) => (
-              <div className="layout-page-side" style={{ top }}>
-                <div className="layout-page-side-inner">
-                  <div className="layout-page-filters">
-                    <ListHeader
-                      canCreate={canCreate}
-                      organization={organization}
-                      refreshQualityGates={this.fetchQualityGates}
-                    />
-                    {qualityGates.length > 0 && (
-                      <List organization={organization} qualityGates={qualityGates} />
-                    )}
-                  </div>
-                </div>
-              </div>
-            )}
-          </ScreenPositionHelper>
-          {!loading &&
-            React.cloneElement(children, {
-              onSetDefault: this.handleSetDefault,
-              organization,
-              qualityGates,
-              refreshQualityGates: this.fetchQualityGates
-            })}
-        </div>
-      </>
-    );
-  }
-}
-
-export default withRouter(QualityGatesApp);
index ba8f1008ec7527d9db29845e936ce3904c18f464..845828351854bf4b3a9867166a266c5365d2dd33 100644 (file)
  */
 import { lazyLoad } from '../../components/lazyLoad';
 
-const routes = [
-  {
-    component: lazyLoad(() => import('./components/QualityGatesApp')),
-    childRoutes: [
-      {
-        indexRoute: { component: lazyLoad(() => import('./components/Intro')) }
-      },
-      {
-        path: 'show/:id',
-        component: lazyLoad(() => import('./components/DetailsApp'))
-      }
-    ]
-  }
-];
+const App = lazyLoad(() => import('./components/App'));
+
+const routes = [{ indexRoute: { component: App } }, { path: 'show/:id', component: App }];
 
 export default routes;