]> source.dussan.org Git - sonarqube.git/commitdiff
rewrite account app in ts
authorStas Vilchik <stas.vilchik@sonarsource.com>
Thu, 20 Sep 2018 10:05:07 +0000 (12:05 +0200)
committerSonarTech <sonartech@sonarsource.com>
Fri, 21 Sep 2018 18:20:55 +0000 (20:20 +0200)
26 files changed:
server/sonar-web/src/main/js/api/users.ts
server/sonar-web/src/main/js/app/components/__tests__/StartupModal-test.tsx
server/sonar-web/src/main/js/app/types.ts
server/sonar-web/src/main/js/apps/account/components/Account.js [deleted file]
server/sonar-web/src/main/js/apps/account/components/Account.tsx [new file with mode: 0644]
server/sonar-web/src/main/js/apps/account/components/Nav.js [deleted file]
server/sonar-web/src/main/js/apps/account/components/Nav.tsx [new file with mode: 0644]
server/sonar-web/src/main/js/apps/account/components/Password.js [deleted file]
server/sonar-web/src/main/js/apps/account/components/Password.tsx [new file with mode: 0644]
server/sonar-web/src/main/js/apps/account/components/Security.js [deleted file]
server/sonar-web/src/main/js/apps/account/components/Security.tsx [new file with mode: 0644]
server/sonar-web/src/main/js/apps/account/components/UserCard.js [deleted file]
server/sonar-web/src/main/js/apps/account/components/UserCard.tsx [new file with mode: 0644]
server/sonar-web/src/main/js/apps/account/profile/Profile.js [deleted file]
server/sonar-web/src/main/js/apps/account/profile/Profile.tsx [new file with mode: 0644]
server/sonar-web/src/main/js/apps/account/profile/UserExternalIdentity.js [deleted file]
server/sonar-web/src/main/js/apps/account/profile/UserExternalIdentity.tsx [new file with mode: 0644]
server/sonar-web/src/main/js/apps/account/profile/UserGroups.js [deleted file]
server/sonar-web/src/main/js/apps/account/profile/UserGroups.tsx [new file with mode: 0644]
server/sonar-web/src/main/js/apps/account/profile/UserScmAccounts.js [deleted file]
server/sonar-web/src/main/js/apps/account/profile/UserScmAccounts.tsx [new file with mode: 0644]
server/sonar-web/src/main/js/apps/projects/create/__tests__/CreateProjectPage-test.tsx
server/sonar-web/src/main/js/apps/projects/create/__tests__/ManualProjectCreate-test.tsx
server/sonar-web/src/main/js/apps/projects/create/__tests__/__snapshots__/CreateProjectPage-test.tsx.snap
server/sonar-web/src/main/js/apps/tutorials/analyzeProject/__tests__/AnalyzeTutorial-test.tsx
server/sonar-web/src/main/js/apps/tutorials/analyzeProject/__tests__/__snapshots__/AnalyzeTutorial-test.tsx.snap

index f266f79bd41ecfc5109e116f088bc524776984aa..1954f192c5067fdc3380e1cf425c92d0b8312141 100644 (file)
@@ -29,8 +29,8 @@ export function changePassword(data: {
   login: string;
   password: string;
   previousPassword?: string;
-}): Promise<void> {
-  return post('/api/users/change_password', data);
+}) {
+  return post('/api/users/change_password', data).catch(throwGlobalError);
 }
 
 export interface UserGroup {
index 7a1e2bf2d3d440097f23fb66ebdee4a4a24163e2..374be478fcd4d94119ecf751a5bb4cff3b67121a 100644 (file)
@@ -48,9 +48,11 @@ jest.mock('../../../helpers/dates', () => ({
 }));
 
 const LOGGED_IN_USER: LoggedInUser = {
+  groups: [],
   isLoggedIn: true,
   login: 'luke',
   name: 'Skywalker',
+  scmAccounts: [],
   showOnboardingTutorial: false
 };
 
index 254817b743036e75896c567817ba070856793099..4df5d148def3db2de6978a82bd083d13f5da4b9b 100644 (file)
@@ -362,10 +362,13 @@ export interface LoggedInUser extends CurrentUser {
   email?: string;
   externalIdentity?: string;
   externalProvider?: string;
+  groups: string[];
   homepage?: HomePage;
   isLoggedIn: true;
+  local?: boolean;
   login: string;
   name: string;
+  scmAccounts: string[];
 }
 
 export interface LongLivingBranch extends Branch {
diff --git a/server/sonar-web/src/main/js/apps/account/components/Account.js b/server/sonar-web/src/main/js/apps/account/components/Account.js
deleted file mode 100644 (file)
index bb4e864..0000000
+++ /dev/null
@@ -1,68 +0,0 @@
-/*
- * SonarQube
- * Copyright (C) 2009-2018 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 { connect } from 'react-redux';
-import Helmet from 'react-helmet';
-import Nav from './Nav';
-import UserCard from './UserCard';
-import { getCurrentUser, areThereCustomOrganizations } from '../../../store/rootReducer';
-import { translate } from '../../../helpers/l10n';
-import handleRequiredAuthentication from '../../../app/utils/handleRequiredAuthentication';
-import Suggestions from '../../../app/components/embed-docs-modal/Suggestions';
-import '../account.css';
-
-class Account extends React.PureComponent {
-  componentDidMount() {
-    if (!this.props.currentUser.isLoggedIn) {
-      handleRequiredAuthentication();
-    }
-  }
-
-  render() {
-    const { currentUser, children } = this.props;
-
-    if (!currentUser.isLoggedIn) {
-      return null;
-    }
-
-    const title = translate('my_account.page');
-    return (
-      <div id="account-page">
-        <Suggestions suggestions="account" />
-        <Helmet defaultTitle={title} titleTemplate={'%s - ' + title} />
-        <header className="account-header">
-          <div className="account-container clearfix">
-            <UserCard user={currentUser} />
-            <Nav customOrganizations={this.props.customOrganizations} user={currentUser} />
-          </div>
-        </header>
-
-        {children}
-      </div>
-    );
-  }
-}
-
-const mapStateToProps = state => ({
-  currentUser: getCurrentUser(state),
-  customOrganizations: areThereCustomOrganizations(state)
-});
-
-export default connect(mapStateToProps)(Account);
diff --git a/server/sonar-web/src/main/js/apps/account/components/Account.tsx b/server/sonar-web/src/main/js/apps/account/components/Account.tsx
new file mode 100644 (file)
index 0000000..02711b9
--- /dev/null
@@ -0,0 +1,74 @@
+/*
+ * SonarQube
+ * Copyright (C) 2009-2018 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 { connect } from 'react-redux';
+import Helmet from 'react-helmet';
+import Nav from './Nav';
+import UserCard from './UserCard';
+import { CurrentUser, LoggedInUser } from '../../../app/types';
+import { getCurrentUser, areThereCustomOrganizations, Store } from '../../../store/rootReducer';
+import { translate } from '../../../helpers/l10n';
+import handleRequiredAuthentication from '../../../app/utils/handleRequiredAuthentication';
+import Suggestions from '../../../app/components/embed-docs-modal/Suggestions';
+import '../account.css';
+
+interface Props {
+  currentUser: CurrentUser;
+  customOrganizations?: boolean;
+}
+
+class Account extends React.PureComponent<Props> {
+  componentDidMount() {
+    if (!this.props.currentUser.isLoggedIn) {
+      handleRequiredAuthentication();
+    }
+  }
+
+  render() {
+    const { currentUser, children } = this.props;
+
+    if (!currentUser.isLoggedIn) {
+      return null;
+    }
+
+    const title = translate('my_account.page');
+    return (
+      <div id="account-page">
+        <Suggestions suggestions="account" />
+        <Helmet defaultTitle={title} titleTemplate={'%s - ' + title} />
+        <header className="account-header">
+          <div className="account-container clearfix">
+            <UserCard user={currentUser as LoggedInUser} />
+            <Nav customOrganizations={this.props.customOrganizations} />
+          </div>
+        </header>
+
+        {children}
+      </div>
+    );
+  }
+}
+
+const mapStateToProps = (state: Store) => ({
+  currentUser: getCurrentUser(state),
+  customOrganizations: areThereCustomOrganizations(state)
+});
+
+export default connect(mapStateToProps)(Account);
diff --git a/server/sonar-web/src/main/js/apps/account/components/Nav.js b/server/sonar-web/src/main/js/apps/account/components/Nav.js
deleted file mode 100644 (file)
index 8688985..0000000
+++ /dev/null
@@ -1,68 +0,0 @@
-/*
- * SonarQube
- * Copyright (C) 2009-2018 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 { Link, IndexLink } from 'react-router';
-import NavBarTabs from '../../../components/nav/NavBarTabs';
-import { translate } from '../../../helpers/l10n';
-
-/*::
-type Props = {
-  customOrganizations: boolean
-};
-*/
-
-export default function Nav({ customOrganizations } /*: Props */) {
-  return (
-    <nav className="account-nav">
-      <NavBarTabs>
-        <li>
-          <IndexLink activeClassName="active" to="/account/">
-            {translate('my_account.profile')}
-          </IndexLink>
-        </li>
-        <li>
-          <Link activeClassName="active" to="/account/security/">
-            {translate('my_account.security')}
-          </Link>
-        </li>
-        <li>
-          <Link activeClassName="active" to="/account/notifications">
-            {translate('my_account.notifications')}
-          </Link>
-        </li>
-        {!customOrganizations && (
-          <li>
-            <Link activeClassName="active" to="/account/projects/">
-              {translate('my_account.projects')}
-            </Link>
-          </li>
-        )}
-        {customOrganizations && (
-          <li>
-            <Link activeClassName="active" to="/account/organizations">
-              {translate('my_account.organizations')}
-            </Link>
-          </li>
-        )}
-      </NavBarTabs>
-    </nav>
-  );
-}
diff --git a/server/sonar-web/src/main/js/apps/account/components/Nav.tsx b/server/sonar-web/src/main/js/apps/account/components/Nav.tsx
new file mode 100644 (file)
index 0000000..304ddb1
--- /dev/null
@@ -0,0 +1,65 @@
+/*
+ * SonarQube
+ * Copyright (C) 2009-2018 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 { Link, IndexLink } from 'react-router';
+import NavBarTabs from '../../../components/nav/NavBarTabs';
+import { translate } from '../../../helpers/l10n';
+
+interface Props {
+  customOrganizations?: boolean;
+}
+
+export default function Nav({ customOrganizations }: Props) {
+  return (
+    <nav className="account-nav">
+      <NavBarTabs>
+        <li>
+          <IndexLink activeClassName="active" to="/account/">
+            {translate('my_account.profile')}
+          </IndexLink>
+        </li>
+        <li>
+          <Link activeClassName="active" to="/account/security/">
+            {translate('my_account.security')}
+          </Link>
+        </li>
+        <li>
+          <Link activeClassName="active" to="/account/notifications">
+            {translate('my_account.notifications')}
+          </Link>
+        </li>
+        {!customOrganizations && (
+          <li>
+            <Link activeClassName="active" to="/account/projects/">
+              {translate('my_account.projects')}
+            </Link>
+          </li>
+        )}
+        {customOrganizations && (
+          <li>
+            <Link activeClassName="active" to="/account/organizations">
+              {translate('my_account.organizations')}
+            </Link>
+          </li>
+        )}
+      </NavBarTabs>
+    </nav>
+  );
+}
diff --git a/server/sonar-web/src/main/js/apps/account/components/Password.js b/server/sonar-web/src/main/js/apps/account/components/Password.js
deleted file mode 100644 (file)
index 00c12a5..0000000
+++ /dev/null
@@ -1,140 +0,0 @@
-/*
- * SonarQube
- * Copyright (C) 2009-2018 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, { Component } from 'react';
-import { changePassword } from '../../../api/users';
-import { SubmitButton } from '../../../components/ui/buttons';
-import { translate } from '../../../helpers/l10n';
-
-export default class Password extends Component {
-  state = {
-    success: false,
-    errors: null
-  };
-
-  handleSuccessfulChange = () => {
-    this.oldPassword.value = '';
-    this.password.value = '';
-    this.passwordConfirmation.value = '';
-    this.setState({ success: true, errors: null });
-  };
-
-  handleFailedChange = e => {
-    e.response.json().then(r => {
-      this.oldPassword.focus();
-      this.setErrors(r.errors.map(e => e.msg));
-    });
-  };
-
-  setErrors = errors => {
-    this.setState({
-      success: false,
-      errors
-    });
-  };
-
-  handleChangePassword = e => {
-    e.preventDefault();
-
-    const { user } = this.props;
-    const previousPassword = this.oldPassword.value;
-    const password = this.password.value;
-    const passwordConfirmation = this.passwordConfirmation.value;
-
-    if (password !== passwordConfirmation) {
-      this.password.focus();
-      this.setErrors([translate('user.password_doesnt_match_confirmation')]);
-    } else {
-      changePassword({ login: user.login, password, previousPassword })
-        .then(this.handleSuccessfulChange)
-        .catch(this.handleFailedChange);
-    }
-  };
-
-  render() {
-    const { success, errors } = this.state;
-
-    return (
-      <section className="boxed-group">
-        <h2 className="spacer-bottom">{translate('my_profile.password.title')}</h2>
-
-        <form className="boxed-group-inner" onSubmit={this.handleChangePassword}>
-          {success && (
-            <div className="alert alert-success">{translate('my_profile.password.changed')}</div>
-          )}
-
-          {errors &&
-            errors.map((e, i) => (
-              <div className="alert alert-danger" key={i}>
-                {e}
-              </div>
-            ))}
-
-          <div className="modal-field">
-            <label htmlFor="old_password">
-              {translate('my_profile.password.old')}
-              <em className="mandatory">*</em>
-            </label>
-            <input
-              autoComplete="off"
-              id="old_password"
-              name="old_password"
-              ref={elem => (this.oldPassword = elem)}
-              required={true}
-              type="password"
-            />
-          </div>
-          <div className="modal-field">
-            <label htmlFor="password">
-              {translate('my_profile.password.new')}
-              <em className="mandatory">*</em>
-            </label>
-            <input
-              autoComplete="off"
-              id="password"
-              name="password"
-              ref={elem => (this.password = elem)}
-              required={true}
-              type="password"
-            />
-          </div>
-          <div className="modal-field">
-            <label htmlFor="password_confirmation">
-              {translate('my_profile.password.confirm')}
-              <em className="mandatory">*</em>
-            </label>
-            <input
-              autoComplete="off"
-              id="password_confirmation"
-              name="password_confirmation"
-              ref={elem => (this.passwordConfirmation = elem)}
-              required={true}
-              type="password"
-            />
-          </div>
-          <div className="modal-field">
-            <SubmitButton id="change-password">
-              {translate('my_profile.password.submit')}
-            </SubmitButton>
-          </div>
-        </form>
-      </section>
-    );
-  }
-}
diff --git a/server/sonar-web/src/main/js/apps/account/components/Password.tsx b/server/sonar-web/src/main/js/apps/account/components/Password.tsx
new file mode 100644 (file)
index 0000000..5834e14
--- /dev/null
@@ -0,0 +1,143 @@
+/*
+ * SonarQube
+ * Copyright (C) 2009-2018 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 { changePassword } from '../../../api/users';
+import { SubmitButton } from '../../../components/ui/buttons';
+import { translate } from '../../../helpers/l10n';
+import { LoggedInUser } from '../../../app/types';
+
+interface Props {
+  user: LoggedInUser;
+}
+
+interface State {
+  errors?: string[];
+  success: boolean;
+}
+
+export default class Password extends React.Component<Props, State> {
+  oldPassword!: HTMLInputElement;
+  password!: HTMLInputElement;
+  passwordConfirmation!: HTMLInputElement;
+  state: State = {
+    success: false
+  };
+
+  handleSuccessfulChange = () => {
+    this.oldPassword.value = '';
+    this.password.value = '';
+    this.passwordConfirmation.value = '';
+    this.setState({ success: true, errors: undefined });
+  };
+
+  setErrors = (errors: string[]) => {
+    this.setState({ success: false, errors });
+  };
+
+  handleChangePassword = (event: React.FormEvent) => {
+    event.preventDefault();
+
+    const { user } = this.props;
+    const previousPassword = this.oldPassword.value;
+    const password = this.password.value;
+    const passwordConfirmation = this.passwordConfirmation.value;
+
+    if (password !== passwordConfirmation) {
+      this.password.focus();
+      this.setErrors([translate('user.password_doesnt_match_confirmation')]);
+    } else {
+      changePassword({ login: user.login, password, previousPassword }).then(
+        this.handleSuccessfulChange,
+        () => {}
+      );
+    }
+  };
+
+  render() {
+    const { success, errors } = this.state;
+
+    return (
+      <section className="boxed-group">
+        <h2 className="spacer-bottom">{translate('my_profile.password.title')}</h2>
+
+        <form className="boxed-group-inner" onSubmit={this.handleChangePassword}>
+          {success && (
+            <div className="alert alert-success">{translate('my_profile.password.changed')}</div>
+          )}
+
+          {errors &&
+            errors.map((e, i) => (
+              <div className="alert alert-danger" key={i}>
+                {e}
+              </div>
+            ))}
+
+          <div className="modal-field">
+            <label htmlFor="old_password">
+              {translate('my_profile.password.old')}
+              <em className="mandatory">*</em>
+            </label>
+            <input
+              autoComplete="off"
+              id="old_password"
+              name="old_password"
+              ref={elem => (this.oldPassword = elem!)}
+              required={true}
+              type="password"
+            />
+          </div>
+          <div className="modal-field">
+            <label htmlFor="password">
+              {translate('my_profile.password.new')}
+              <em className="mandatory">*</em>
+            </label>
+            <input
+              autoComplete="off"
+              id="password"
+              name="password"
+              ref={elem => (this.password = elem!)}
+              required={true}
+              type="password"
+            />
+          </div>
+          <div className="modal-field">
+            <label htmlFor="password_confirmation">
+              {translate('my_profile.password.confirm')}
+              <em className="mandatory">*</em>
+            </label>
+            <input
+              autoComplete="off"
+              id="password_confirmation"
+              name="password_confirmation"
+              ref={elem => (this.passwordConfirmation = elem!)}
+              required={true}
+              type="password"
+            />
+          </div>
+          <div className="modal-field">
+            <SubmitButton id="change-password">
+              {translate('my_profile.password.submit')}
+            </SubmitButton>
+          </div>
+        </form>
+      </section>
+    );
+  }
+}
diff --git a/server/sonar-web/src/main/js/apps/account/components/Security.js b/server/sonar-web/src/main/js/apps/account/components/Security.js
deleted file mode 100644 (file)
index af77c5e..0000000
+++ /dev/null
@@ -1,40 +0,0 @@
-/*
- * SonarQube
- * Copyright (C) 2009-2018 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 Helmet from 'react-helmet';
-import { connect } from 'react-redux';
-import Password from './Password';
-import Tokens from './Tokens';
-import { translate } from '../../../helpers/l10n';
-import { getCurrentUser } from '../../../store/rootReducer';
-
-function Security(props) {
-  const { user } = props;
-
-  return (
-    <div className="account-body account-container">
-      <Helmet title={translate('my_account.security')} />
-      <Tokens login={user.login} />
-      {user.local && <Password user={user} />}
-    </div>
-  );
-}
-
-export default connect(state => ({ user: getCurrentUser(state) }))(Security);
diff --git a/server/sonar-web/src/main/js/apps/account/components/Security.tsx b/server/sonar-web/src/main/js/apps/account/components/Security.tsx
new file mode 100644 (file)
index 0000000..bca8526
--- /dev/null
@@ -0,0 +1,47 @@
+/*
+ * SonarQube
+ * Copyright (C) 2009-2018 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 Password from './Password';
+import Tokens from './Tokens';
+import { translate } from '../../../helpers/l10n';
+import { getCurrentUser, Store } from '../../../store/rootReducer';
+import { LoggedInUser } from '../../../app/types';
+
+interface Props {
+  user: LoggedInUser;
+}
+
+function Security({ user }: Props) {
+  return (
+    <div className="account-body account-container">
+      <Helmet title={translate('my_account.security')} />
+      <Tokens login={user.login} />
+      {user.local && <Password user={user} />}
+    </div>
+  );
+}
+
+const mapStateToProps = (state: Store) => ({
+  user: getCurrentUser(state) as LoggedInUser
+});
+
+export default connect(mapStateToProps)(Security);
diff --git a/server/sonar-web/src/main/js/apps/account/components/UserCard.js b/server/sonar-web/src/main/js/apps/account/components/UserCard.js
deleted file mode 100644 (file)
index a67a2ef..0000000
+++ /dev/null
@@ -1,43 +0,0 @@
-/*
- * SonarQube
- * Copyright (C) 2009-2018 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 PropTypes from 'prop-types';
-import Avatar from '../../../components/ui/Avatar';
-
-export default class UserCard extends React.PureComponent {
-  static propTypes = {
-    user: PropTypes.object.isRequired
-  };
-
-  render() {
-    const { user } = this.props;
-
-    return (
-      <div className="account-user">
-        <div className="pull-left account-user-avatar" id="avatar">
-          <Avatar hash={user.avatar} name={user.name} size={60} />
-        </div>
-        <h1 className="pull-left" id="name">
-          {user.name}
-        </h1>
-      </div>
-    );
-  }
-}
diff --git a/server/sonar-web/src/main/js/apps/account/components/UserCard.tsx b/server/sonar-web/src/main/js/apps/account/components/UserCard.tsx
new file mode 100644 (file)
index 0000000..6afeeaa
--- /dev/null
@@ -0,0 +1,39 @@
+/*
+ * SonarQube
+ * Copyright (C) 2009-2018 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 Avatar from '../../../components/ui/Avatar';
+import { LoggedInUser } from '../../../app/types';
+
+interface Props {
+  user: LoggedInUser;
+}
+
+export default function UserCard({ user }: Props) {
+  return (
+    <div className="account-user">
+      <div className="pull-left account-user-avatar" id="avatar">
+        <Avatar hash={user.avatar} name={user.name} size={60} />
+      </div>
+      <h1 className="pull-left" id="name">
+        {user.name}
+      </h1>
+    </div>
+  );
+}
diff --git a/server/sonar-web/src/main/js/apps/account/profile/Profile.js b/server/sonar-web/src/main/js/apps/account/profile/Profile.js
deleted file mode 100644 (file)
index 3b91c76..0000000
+++ /dev/null
@@ -1,82 +0,0 @@
-/*
- * SonarQube
- * Copyright (C) 2009-2018 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 { connect } from 'react-redux';
-import UserExternalIdentity from './UserExternalIdentity';
-import UserGroups from './UserGroups';
-import UserScmAccounts from './UserScmAccounts';
-import { getCurrentUser, areThereCustomOrganizations } from '../../../store/rootReducer';
-import { translate } from '../../../helpers/l10n';
-
-/*::
-type Props = {
-  customOrganizations: boolean,
-  user: {
-    email?: string,
-    externalProvider?: string,
-    groups: Array<*>,
-    local: boolean,
-    login: string,
-    scmAccounts: Array<*>
-  }
-};
-*/
-
-function Profile(props /*: Props */) {
-  const { customOrganizations, user } = props;
-
-  return (
-    <div className="account-body account-container">
-      <div className="boxed-group boxed-group-inner">
-        <div className="spacer-bottom">
-          {translate('login')}: <strong id="login">{user.login}</strong>
-        </div>
-
-        {!user.local &&
-          user.externalProvider !== 'sonarqube' && (
-            <div className="spacer-bottom" id="identity-provider">
-              <UserExternalIdentity user={user} />
-            </div>
-          )}
-
-        {!!user.email && (
-          <div className="spacer-bottom">
-            {translate('my_profile.email')}: <strong id="email">{user.email}</strong>
-          </div>
-        )}
-
-        {!customOrganizations && <hr className="account-separator" />}
-        {!customOrganizations && <UserGroups groups={user.groups} />}
-
-        <hr />
-
-        <UserScmAccounts scmAccounts={user.scmAccounts} user={user} />
-      </div>
-    </div>
-  );
-}
-
-const mapStateToProps = state => ({
-  customOrganizations: areThereCustomOrganizations(state),
-  user: getCurrentUser(state)
-});
-
-export default connect(mapStateToProps)(Profile);
diff --git a/server/sonar-web/src/main/js/apps/account/profile/Profile.tsx b/server/sonar-web/src/main/js/apps/account/profile/Profile.tsx
new file mode 100644 (file)
index 0000000..f03d64c
--- /dev/null
@@ -0,0 +1,75 @@
+/*
+ * SonarQube
+ * Copyright (C) 2009-2018 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 { connect } from 'react-redux';
+import UserExternalIdentity from './UserExternalIdentity';
+import UserGroups from './UserGroups';
+import UserScmAccounts from './UserScmAccounts';
+import { getCurrentUser, areThereCustomOrganizations, Store } from '../../../store/rootReducer';
+import { translate } from '../../../helpers/l10n';
+import { LoggedInUser } from '../../../app/types';
+
+interface Props {
+  customOrganizations?: boolean;
+  user: LoggedInUser;
+}
+
+function Profile({ customOrganizations, user }: Props) {
+  return (
+    <div className="account-body account-container">
+      <div className="boxed-group boxed-group-inner">
+        <div className="spacer-bottom">
+          {translate('login')}: <strong id="login">{user.login}</strong>
+        </div>
+
+        {!user.local &&
+          user.externalProvider !== 'sonarqube' && (
+            <div className="spacer-bottom" id="identity-provider">
+              <UserExternalIdentity user={user} />
+            </div>
+          )}
+
+        {!!user.email && (
+          <div className="spacer-bottom">
+            {translate('my_profile.email')}: <strong id="email">{user.email}</strong>
+          </div>
+        )}
+
+        {!customOrganizations && (
+          <>
+            <hr className="account-separator" />
+            <UserGroups groups={user.groups} />
+          </>
+        )}
+
+        <hr />
+
+        <UserScmAccounts scmAccounts={user.scmAccounts} user={user} />
+      </div>
+    </div>
+  );
+}
+
+const mapStateToProps = (state: Store) => ({
+  customOrganizations: areThereCustomOrganizations(state),
+  user: getCurrentUser(state) as LoggedInUser
+});
+
+export default connect(mapStateToProps)(Profile);
diff --git a/server/sonar-web/src/main/js/apps/account/profile/UserExternalIdentity.js b/server/sonar-web/src/main/js/apps/account/profile/UserExternalIdentity.js
deleted file mode 100644 (file)
index 8e0536f..0000000
+++ /dev/null
@@ -1,100 +0,0 @@
-/*
- * SonarQube
- * Copyright (C) 2009-2018 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 { getIdentityProviders } from '../../../api/users';
-import * as theme from '../../../app/theme';
-import { getTextColor } from '../../../helpers/colors';
-
-export default class UserExternalIdentity extends React.PureComponent {
-  state = {
-    loading: true
-  };
-
-  componentDidMount() {
-    this.mounted = true;
-    this.fetchIdentityProviders();
-  }
-
-  componentDidUpdate(prevProps) {
-    if (prevProps.user !== this.props.user) {
-      this.fetchIdentityProviders();
-    }
-  }
-
-  componentWillUnmount() {
-    this.mounted = false;
-  }
-
-  fetchIdentityProviders() {
-    this.setState({ loading: true });
-    getIdentityProviders()
-      .then(r => r.identityProviders)
-      .then(providers => {
-        if (this.mounted) {
-          const identityProvider = providers.find(
-            provider => provider.key === this.props.user.externalProvider
-          );
-          this.setState({ loading: false, identityProvider });
-        }
-      })
-      .catch(() => {
-        if (this.mounted) {
-          this.setState({ loading: false });
-        }
-      });
-  }
-
-  render() {
-    const { user } = this.props;
-    const { loading, identityProvider } = this.state;
-
-    if (loading) {
-      return null;
-    }
-
-    if (!identityProvider) {
-      return (
-        <div>
-          {user.externalProvider}
-          {': '}
-          {user.externalIdentity}
-        </div>
-      );
-    }
-
-    return (
-      <div
-        className="identity-provider"
-        style={{
-          backgroundColor: identityProvider.backgroundColor,
-          color: getTextColor(identityProvider.backgroundColor, theme.secondFontColor)
-        }}>
-        <img
-          alt={identityProvider.name}
-          className="little-spacer-right"
-          height="14"
-          src={window.baseUrl + identityProvider.iconPath}
-          width="14"
-        />{' '}
-        {user.externalIdentity}
-      </div>
-    );
-  }
-}
diff --git a/server/sonar-web/src/main/js/apps/account/profile/UserExternalIdentity.tsx b/server/sonar-web/src/main/js/apps/account/profile/UserExternalIdentity.tsx
new file mode 100644 (file)
index 0000000..a71dad7
--- /dev/null
@@ -0,0 +1,112 @@
+/*
+ * SonarQube
+ * Copyright (C) 2009-2018 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 { getIdentityProviders } from '../../../api/users';
+import * as theme from '../../../app/theme';
+import { getTextColor } from '../../../helpers/colors';
+import { LoggedInUser, IdentityProvider } from '../../../app/types';
+import { getBaseUrl } from '../../../helpers/urls';
+
+interface Props {
+  user: LoggedInUser;
+}
+
+interface State {
+  identityProvider?: IdentityProvider;
+  loading: boolean;
+}
+
+export default class UserExternalIdentity extends React.PureComponent<Props, State> {
+  mounted = false;
+  state: State = {
+    loading: true
+  };
+
+  componentDidMount() {
+    this.mounted = true;
+    this.fetchIdentityProviders();
+  }
+
+  componentDidUpdate(prevProps: Props) {
+    if (prevProps.user !== this.props.user) {
+      this.fetchIdentityProviders();
+    }
+  }
+
+  componentWillUnmount() {
+    this.mounted = false;
+  }
+
+  fetchIdentityProviders() {
+    this.setState({ loading: true });
+    getIdentityProviders()
+      .then(r => r.identityProviders)
+      .then(providers => {
+        if (this.mounted) {
+          const identityProvider = providers.find(
+            provider => provider.key === this.props.user.externalProvider
+          );
+          this.setState({ loading: false, identityProvider });
+        }
+      })
+      .catch(() => {
+        if (this.mounted) {
+          this.setState({ loading: false });
+        }
+      });
+  }
+
+  render() {
+    const { user } = this.props;
+    const { loading, identityProvider } = this.state;
+
+    if (loading) {
+      return null;
+    }
+
+    if (!identityProvider) {
+      return (
+        <div>
+          {user.externalProvider}
+          {': '}
+          {user.externalIdentity}
+        </div>
+      );
+    }
+
+    return (
+      <div
+        className="identity-provider"
+        style={{
+          backgroundColor: identityProvider.backgroundColor,
+          color: getTextColor(identityProvider.backgroundColor, theme.secondFontColor)
+        }}>
+        <img
+          alt={identityProvider.name}
+          className="little-spacer-right"
+          height="14"
+          src={getBaseUrl() + identityProvider.iconPath}
+          width="14"
+        />{' '}
+        {user.externalIdentity}
+      </div>
+    );
+  }
+}
diff --git a/server/sonar-web/src/main/js/apps/account/profile/UserGroups.js b/server/sonar-web/src/main/js/apps/account/profile/UserGroups.js
deleted file mode 100644 (file)
index f81ba23..0000000
+++ /dev/null
@@ -1,45 +0,0 @@
-/*
- * SonarQube
- * Copyright (C) 2009-2018 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 PropTypes from 'prop-types';
-import { translate } from '../../../helpers/l10n';
-
-export default class UserGroups extends React.PureComponent {
-  static propTypes = {
-    groups: PropTypes.arrayOf(PropTypes.string).isRequired
-  };
-
-  render() {
-    const { groups } = this.props;
-
-    return (
-      <div>
-        <h2 className="spacer-bottom">{translate('my_profile.groups')}</h2>
-        <ul id="groups">
-          {groups.map(group => (
-            <li className="little-spacer-bottom" key={group} title={group}>
-              {group}
-            </li>
-          ))}
-        </ul>
-      </div>
-    );
-  }
-}
diff --git a/server/sonar-web/src/main/js/apps/account/profile/UserGroups.tsx b/server/sonar-web/src/main/js/apps/account/profile/UserGroups.tsx
new file mode 100644 (file)
index 0000000..a8a5e23
--- /dev/null
@@ -0,0 +1,40 @@
+/*
+ * SonarQube
+ * Copyright (C) 2009-2018 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 { translate } from '../../../helpers/l10n';
+
+interface Props {
+  groups: string[];
+}
+
+export default function UserGroups({ groups }: Props) {
+  return (
+    <div>
+      <h2 className="spacer-bottom">{translate('my_profile.groups')}</h2>
+      <ul id="groups">
+        {groups.map(group => (
+          <li className="little-spacer-bottom" key={group} title={group}>
+            {group}
+          </li>
+        ))}
+      </ul>
+    </div>
+  );
+}
diff --git a/server/sonar-web/src/main/js/apps/account/profile/UserScmAccounts.js b/server/sonar-web/src/main/js/apps/account/profile/UserScmAccounts.js
deleted file mode 100644 (file)
index a306959..0000000
+++ /dev/null
@@ -1,56 +0,0 @@
-/*
- * SonarQube
- * Copyright (C) 2009-2018 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 PropTypes from 'prop-types';
-import { translate } from '../../../helpers/l10n';
-
-export default class UserScmAccounts extends React.PureComponent {
-  static propTypes = {
-    user: PropTypes.object.isRequired,
-    scmAccounts: PropTypes.arrayOf(PropTypes.string).isRequired
-  };
-
-  render() {
-    const { user, scmAccounts } = this.props;
-
-    return (
-      <div>
-        <h2 className="spacer-bottom">{translate('my_profile.scm_accounts')}</h2>
-        <ul id="scm-accounts">
-          <li className="little-spacer-bottom text-ellipsis" title={user.login}>
-            {user.login}
-          </li>
-
-          {user.email && (
-            <li className="little-spacer-bottom text-ellipsis" title={user.email}>
-              {user.email}
-            </li>
-          )}
-
-          {scmAccounts.map(scmAccount => (
-            <li className="little-spacer-bottom" key={scmAccount} title={scmAccount}>
-              {scmAccount}
-            </li>
-          ))}
-        </ul>
-      </div>
-    );
-  }
-}
diff --git a/server/sonar-web/src/main/js/apps/account/profile/UserScmAccounts.tsx b/server/sonar-web/src/main/js/apps/account/profile/UserScmAccounts.tsx
new file mode 100644 (file)
index 0000000..13be6a8
--- /dev/null
@@ -0,0 +1,52 @@
+/*
+ * SonarQube
+ * Copyright (C) 2009-2018 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 { translate } from '../../../helpers/l10n';
+import { LoggedInUser } from '../../../app/types';
+
+interface Props {
+  scmAccounts: string[];
+  user: LoggedInUser;
+}
+
+export default function UserScmAccounts({ user, scmAccounts }: Props) {
+  return (
+    <div>
+      <h2 className="spacer-bottom">{translate('my_profile.scm_accounts')}</h2>
+      <ul id="scm-accounts">
+        <li className="little-spacer-bottom text-ellipsis" title={user.login}>
+          {user.login}
+        </li>
+
+        {user.email && (
+          <li className="little-spacer-bottom text-ellipsis" title={user.email}>
+            {user.email}
+          </li>
+        )}
+
+        {scmAccounts.map(scmAccount => (
+          <li className="little-spacer-bottom" key={scmAccount} title={scmAccount}>
+            {scmAccount}
+          </li>
+        ))}
+      </ul>
+    </div>
+  );
+}
index c4dd1380821777f1299cab95a7f58d2bacaf3203..d20c0fd9ac841e1b4010d3ebe169c5434cea31eb 100644 (file)
@@ -40,9 +40,11 @@ jest.mock('../../../../api/users', () => ({
 
 const user: LoggedInUser = {
   externalProvider: 'github',
+  groups: [],
   isLoggedIn: true,
   login: 'foo',
-  name: 'Foo'
+  name: 'Foo',
+  scmAccounts: []
 };
 
 beforeEach(() => {
index f67db69e464def67490d548cd2cc382fdee393ce..b2051ab3f53cfeb2b2ab4fee6b9a0649d09be6ea 100644 (file)
@@ -69,7 +69,7 @@ it('should correctly create a project', async () => {
 function getWrapper(props = {}) {
   return shallow(
     <ManualProjectCreate
-      currentUser={{ isLoggedIn: true, login: 'foo', name: 'Foo' }}
+      currentUser={{ groups: [], isLoggedIn: true, login: 'foo', name: 'Foo', scmAccounts: [] }}
       fetchMyOrganizations={jest.fn()}
       onProjectCreate={jest.fn()}
       userOrganizations={[{ key: 'foo', name: 'Foo' }, { key: 'bar', name: 'Bar' }]}
index fffc678a6db7609959e9a72ccd39f5ebc3d6f428..bafced94b3ef206d4c8de49ff97af1bed3cfc96c 100644 (file)
@@ -116,9 +116,11 @@ exports[`should render with Manual creation only 1`] = `
         currentUser={
           Object {
             "externalProvider": "microsoft",
+            "groups": Array [],
             "isLoggedIn": true,
             "login": "foo",
             "name": "Foo",
+            "scmAccounts": Array [],
           }
         }
         onProjectCreate={[Function]}
index 39f0233081f7e5709e227cb4288402b91ea8520e..98e26a55df0aa759ff1a791debeedcd88ebc99c2 100644 (file)
@@ -33,9 +33,11 @@ const component = {
 };
 
 const loggedInUser: LoggedInUser = {
+  groups: [],
   isLoggedIn: true,
   login: 'luke',
-  name: 'Skywalker'
+  name: 'Skywalker',
+  scmAccounts: []
 };
 
 it('renders correctly', () => {
index af09b977a369d767be6517281735c7323dcf5168..b7838005879c73d22d95550534e9753ba12fdbe0 100644 (file)
@@ -21,9 +21,11 @@ exports[`renders correctly 1`] = `
     <TokenStep
       currentUser={
         Object {
+          "groups": Array [],
           "isLoggedIn": true,
           "login": "luke",
           "name": "Skywalker",
+          "scmAccounts": Array [],
         }
       }
       finished={false}