diff options
Diffstat (limited to 'server/sonar-web/src/main/js/apps/account')
13 files changed, 380 insertions, 368 deletions
diff --git a/server/sonar-web/src/main/js/apps/account/account.css b/server/sonar-web/src/main/js/apps/account/account.css index 12c3cabf574..73d42282e13 100644 --- a/server/sonar-web/src/main/js/apps/account/account.css +++ b/server/sonar-web/src/main/js/apps/account/account.css @@ -8,7 +8,7 @@ padding-top: 20px; padding-bottom: 20px; border-bottom: 1px solid var(--barBorderColor); - background-color: var(--barBackgroundColor); + background-color: #fff; } .account-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 index dec082bc69b..6e3bb3c3c69 100644 --- a/server/sonar-web/src/main/js/apps/account/components/Password.js +++ b/server/sonar-web/src/main/js/apps/account/components/Password.js @@ -70,10 +70,10 @@ export default class Password extends Component { const { success, errors } = this.state; return ( - <section> + <section className="boxed-group"> <h2 className="spacer-bottom">{translate('my_profile.password.title')}</h2> - <form onSubmit={this.handleChangePassword}> + <form className="boxed-group-inner" onSubmit={this.handleChangePassword}> {success && ( <div className="alert alert-success">{translate('my_profile.password.changed')}</div> )} 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 index d52a3b6e1ea..3006ca55f1a 100644 --- a/server/sonar-web/src/main/js/apps/account/components/Security.js +++ b/server/sonar-web/src/main/js/apps/account/components/Security.js @@ -31,11 +31,7 @@ function Security(props) { return ( <div className="account-body account-container"> <Helmet title={translate('my_account.security')} /> - <Tokens user={user} /> - - {user.local && <hr className="account-separator" />} - {user.local && <Password user={user} />} </div> ); diff --git a/server/sonar-web/src/main/js/apps/account/notifications/GlobalNotifications.js b/server/sonar-web/src/main/js/apps/account/notifications/GlobalNotifications.js index d7ba3d35197..294c2c209c6 100644 --- a/server/sonar-web/src/main/js/apps/account/notifications/GlobalNotifications.js +++ b/server/sonar-web/src/main/js/apps/account/notifications/GlobalNotifications.js @@ -46,30 +46,32 @@ type Props = { function GlobalNotifications(props /*: Props */) { return ( - <section> - <h2 className="spacer-bottom">{translate('my_profile.overall_notifications.title')}</h2> + <section className="boxed-group"> + <h2>{translate('my_profile.overall_notifications.title')}</h2> - <table className="form"> - <thead> - <tr> - <th /> - {props.channels.map(channel => ( - <th key={channel} className="text-center"> - <h4>{translate('notification.channel', channel)}</h4> - </th> - ))} - </tr> - </thead> + <div className="boxed-group-inner"> + <table className="form"> + <thead> + <tr> + <th /> + {props.channels.map(channel => ( + <th key={channel} className="text-center"> + <h4>{translate('notification.channel', channel)}</h4> + </th> + ))} + </tr> + </thead> - <NotificationsList - notifications={props.notifications} - channels={props.channels} - types={props.types} - checkboxId={(d, c) => `global-notification-${d}-${c}`} - onAdd={props.addNotification} - onRemove={props.removeNotification} - /> - </table> + <NotificationsList + notifications={props.notifications} + channels={props.channels} + types={props.types} + checkboxId={(d, c) => `global-notification-${d}-${c}`} + onAdd={props.addNotification} + onRemove={props.removeNotification} + /> + </table> + </div> </section> ); } diff --git a/server/sonar-web/src/main/js/apps/account/notifications/Notifications.js b/server/sonar-web/src/main/js/apps/account/notifications/Notifications.js index aa2c8d1179e..6e5fed53bcc 100644 --- a/server/sonar-web/src/main/js/apps/account/notifications/Notifications.js +++ b/server/sonar-web/src/main/js/apps/account/notifications/Notifications.js @@ -40,13 +40,8 @@ class Notifications extends React.PureComponent { return ( <div className="account-body account-container"> <Helmet title={translate('my_account.notifications')} /> - - <p className="big-spacer-bottom">{translate('notification.dispatcher.information')}</p> - + <p className="alert alert-info">{translate('notification.dispatcher.information')}</p> <GlobalNotifications /> - - <hr className="account-separator" /> - <Projects /> </div> ); diff --git a/server/sonar-web/src/main/js/apps/account/notifications/Projects.js b/server/sonar-web/src/main/js/apps/account/notifications/Projects.js index 14a7f74020c..5345b55591a 100644 --- a/server/sonar-web/src/main/js/apps/account/notifications/Projects.js +++ b/server/sonar-web/src/main/js/apps/account/notifications/Projects.js @@ -113,30 +113,32 @@ class Projects extends React.PureComponent { const allProjects = [...this.props.projects, ...this.state.addedProjects]; return ( - <section> - <h2 className="spacer-bottom">{translate('my_profile.per_project_notifications.title')}</h2> - - {allProjects.length === 0 && ( - <div className="note">{translate('my_account.no_project_notifications')}</div> - )} - - {allProjects.map(project => <ProjectNotifications key={project.key} project={project} />)} - - <div className="spacer-top panel bg-muted"> - <span className="text-middle spacer-right"> - {translate('my_account.set_notifications_for')}: - </span> - <AsyncSelect - autoload={false} - cache={false} - name="new_project" - style={{ width: '300px' }} - loadOptions={this.loadOptions} - minimumInput={2} - optionRenderer={this.renderOption} - onChange={this.handleAddProject} - placeholder={translate('my_account.search_project')} - /> + <section className="boxed-group"> + <h2>{translate('my_profile.per_project_notifications.title')}</h2> + + <div className="boxed-group-inner"> + {allProjects.length === 0 && ( + <div className="note">{translate('my_account.no_project_notifications')}</div> + )} + + {allProjects.map(project => <ProjectNotifications key={project.key} project={project} />)} + + <div className="spacer-top panel bg-muted"> + <span className="text-middle spacer-right"> + {translate('my_account.set_notifications_for')}: + </span> + <AsyncSelect + autoload={false} + cache={false} + name="new_project" + style={{ width: '300px' }} + loadOptions={this.loadOptions} + minimumInput={2} + optionRenderer={this.renderOption} + onChange={this.handleAddProject} + placeholder={translate('my_account.search_project')} + /> + </div> </div> </section> ); diff --git a/server/sonar-web/src/main/js/apps/account/notifications/__tests__/__snapshots__/GlobalNotifications-test.js.snap b/server/sonar-web/src/main/js/apps/account/notifications/__tests__/__snapshots__/GlobalNotifications-test.js.snap index 4a7d7d10c5a..64a62011d96 100644 --- a/server/sonar-web/src/main/js/apps/account/notifications/__tests__/__snapshots__/GlobalNotifications-test.js.snap +++ b/server/sonar-web/src/main/js/apps/account/notifications/__tests__/__snapshots__/GlobalNotifications-test.js.snap @@ -1,69 +1,73 @@ // Jest Snapshot v1, https://goo.gl/fbAQLP exports[`should match snapshot 1`] = ` -<section> - <h2 - className="spacer-bottom" - > +<section + className="boxed-group" +> + <h2> my_profile.overall_notifications.title </h2> - <table - className="form" + <div + className="boxed-group-inner" > - <thead> - <tr> - <th /> - <th - className="text-center" - key="channel1" - > - <h4> - notification.channel.channel1 - </h4> - </th> - <th - className="text-center" - key="channel2" - > - <h4> - notification.channel.channel2 - </h4> - </th> - </tr> - </thead> - <NotificationsList - channels={ - Array [ - "channel1", - "channel2", - ] - } - checkboxId={[Function]} - notifications={ - Array [ - Object { - "channel": "channel1", - "type": "type1", - }, - Object { - "channel": "channel1", - "type": "type2", - }, - Object { - "channel": "channel2", - "type": "type2", - }, - ] - } - onAdd={[Function]} - onRemove={[Function]} - types={ - Array [ - "type1", - "type2", - ] - } - /> - </table> + <table + className="form" + > + <thead> + <tr> + <th /> + <th + className="text-center" + key="channel1" + > + <h4> + notification.channel.channel1 + </h4> + </th> + <th + className="text-center" + key="channel2" + > + <h4> + notification.channel.channel2 + </h4> + </th> + </tr> + </thead> + <NotificationsList + channels={ + Array [ + "channel1", + "channel2", + ] + } + checkboxId={[Function]} + notifications={ + Array [ + Object { + "channel": "channel1", + "type": "type1", + }, + Object { + "channel": "channel1", + "type": "type2", + }, + Object { + "channel": "channel2", + "type": "type2", + }, + ] + } + onAdd={[Function]} + onRemove={[Function]} + types={ + Array [ + "type1", + "type2", + ] + } + /> + </table> + </div> </section> `; diff --git a/server/sonar-web/src/main/js/apps/account/notifications/__tests__/__snapshots__/Notifications-test.js.snap b/server/sonar-web/src/main/js/apps/account/notifications/__tests__/__snapshots__/Notifications-test.js.snap index 8541cc01be7..855ccecb356 100644 --- a/server/sonar-web/src/main/js/apps/account/notifications/__tests__/__snapshots__/Notifications-test.js.snap +++ b/server/sonar-web/src/main/js/apps/account/notifications/__tests__/__snapshots__/Notifications-test.js.snap @@ -10,14 +10,11 @@ exports[`should match snapshot 1`] = ` title="my_account.notifications" /> <p - className="big-spacer-bottom" + className="alert alert-info" > notification.dispatcher.information </p> <Connect(GlobalNotifications) /> - <hr - className="account-separator" - /> <Connect(Projects) /> </div> `; diff --git a/server/sonar-web/src/main/js/apps/account/notifications/__tests__/__snapshots__/Projects-test.js.snap b/server/sonar-web/src/main/js/apps/account/notifications/__tests__/__snapshots__/Projects-test.js.snap index 3d6b8b28eb1..7f2fb250868 100644 --- a/server/sonar-web/src/main/js/apps/account/notifications/__tests__/__snapshots__/Projects-test.js.snap +++ b/server/sonar-web/src/main/js/apps/account/notifications/__tests__/__snapshots__/Projects-test.js.snap @@ -1,178 +1,190 @@ // Jest Snapshot v1, https://goo.gl/fbAQLP exports[`should render projects 1`] = ` -<section> - <h2 - className="spacer-bottom" - > +<section + className="boxed-group" +> + <h2> my_profile.per_project_notifications.title </h2> - <Connect(ProjectNotifications) - key="foo" - project={ - Object { - "key": "foo", - "name": "Foo", - } - } - /> - <Connect(ProjectNotifications) - key="bar" - project={ - Object { - "key": "bar", - "name": "Bar", - } - } - /> <div - className="spacer-top panel bg-muted" + className="boxed-group-inner" > - <span - className="text-middle spacer-right" - > - my_account.set_notifications_for - : - </span> - <AsyncSelect - autoload={false} - cache={false} - loadOptions={[Function]} - minimumInput={2} - name="new_project" - onChange={[Function]} - optionRenderer={[Function]} - placeholder="my_account.search_project" - style={ + <Connect(ProjectNotifications) + key="foo" + project={ + Object { + "key": "foo", + "name": "Foo", + } + } + /> + <Connect(ProjectNotifications) + key="bar" + project={ Object { - "width": "300px", + "key": "bar", + "name": "Bar", } } /> + <div + className="spacer-top panel bg-muted" + > + <span + className="text-middle spacer-right" + > + my_account.set_notifications_for + : + </span> + <AsyncSelect + autoload={false} + cache={false} + loadOptions={[Function]} + minimumInput={2} + name="new_project" + onChange={[Function]} + optionRenderer={[Function]} + placeholder="my_account.search_project" + style={ + Object { + "width": "300px", + } + } + /> + </div> </div> </section> `; exports[`should render projects 2`] = ` -<section> - <h2 - className="spacer-bottom" - > +<section + className="boxed-group" +> + <h2> my_profile.per_project_notifications.title </h2> - <Connect(ProjectNotifications) - key="foo" - project={ - Object { - "key": "foo", - "name": "Foo", - } - } - /> - <Connect(ProjectNotifications) - key="bar" - project={ - Object { - "key": "bar", - "name": "Bar", - } - } - /> - <Connect(ProjectNotifications) - key="qux" - project={ - Object { - "key": "qux", - "name": "Qux", - } - } - /> <div - className="spacer-top panel bg-muted" + className="boxed-group-inner" > - <span - className="text-middle spacer-right" - > - my_account.set_notifications_for - : - </span> - <AsyncSelect - autoload={false} - cache={false} - loadOptions={[Function]} - minimumInput={2} - name="new_project" - onChange={[Function]} - optionRenderer={[Function]} - placeholder="my_account.search_project" - style={ + <Connect(ProjectNotifications) + key="foo" + project={ + Object { + "key": "foo", + "name": "Foo", + } + } + /> + <Connect(ProjectNotifications) + key="bar" + project={ + Object { + "key": "bar", + "name": "Bar", + } + } + /> + <Connect(ProjectNotifications) + key="qux" + project={ Object { - "width": "300px", + "key": "qux", + "name": "Qux", } } /> + <div + className="spacer-top panel bg-muted" + > + <span + className="text-middle spacer-right" + > + my_account.set_notifications_for + : + </span> + <AsyncSelect + autoload={false} + cache={false} + loadOptions={[Function]} + minimumInput={2} + name="new_project" + onChange={[Function]} + optionRenderer={[Function]} + placeholder="my_account.search_project" + style={ + Object { + "width": "300px", + } + } + /> + </div> </div> </section> `; exports[`should render projects 3`] = ` -<section> - <h2 - className="spacer-bottom" - > +<section + className="boxed-group" +> + <h2> my_profile.per_project_notifications.title </h2> - <Connect(ProjectNotifications) - key="foo" - project={ - Object { - "key": "foo", - "name": "Foo", - } - } - /> - <Connect(ProjectNotifications) - key="bar" - project={ - Object { - "key": "bar", - "name": "Bar", - } - } - /> - <Connect(ProjectNotifications) - key="qux" - project={ - Object { - "key": "qux", - "name": "Qux", - } - } - /> <div - className="spacer-top panel bg-muted" + className="boxed-group-inner" > - <span - className="text-middle spacer-right" - > - my_account.set_notifications_for - : - </span> - <AsyncSelect - autoload={false} - cache={false} - loadOptions={[Function]} - minimumInput={2} - name="new_project" - onChange={[Function]} - optionRenderer={[Function]} - placeholder="my_account.search_project" - style={ + <Connect(ProjectNotifications) + key="foo" + project={ Object { - "width": "300px", + "key": "foo", + "name": "Foo", } } /> + <Connect(ProjectNotifications) + key="bar" + project={ + Object { + "key": "bar", + "name": "Bar", + } + } + /> + <Connect(ProjectNotifications) + key="qux" + project={ + Object { + "key": "qux", + "name": "Qux", + } + } + /> + <div + className="spacer-top panel bg-muted" + > + <span + className="text-middle spacer-right" + > + my_account.set_notifications_for + : + </span> + <AsyncSelect + autoload={false} + cache={false} + loadOptions={[Function]} + minimumInput={2} + name="new_project" + onChange={[Function]} + optionRenderer={[Function]} + placeholder="my_account.search_project" + style={ + Object { + "width": "300px", + } + } + /> + </div> </div> </section> `; diff --git a/server/sonar-web/src/main/js/apps/account/organizations/OrganizationsList.tsx b/server/sonar-web/src/main/js/apps/account/organizations/OrganizationsList.tsx index 5b0b8ccc367..42273f32553 100644 --- a/server/sonar-web/src/main/js/apps/account/organizations/OrganizationsList.tsx +++ b/server/sonar-web/src/main/js/apps/account/organizations/OrganizationsList.tsx @@ -21,12 +21,17 @@ import * as React from 'react'; import { sortBy } from 'lodash'; import OrganizationCard from './OrganizationCard'; import { Organization } from '../../../app/types'; +import { translate } from '../../../helpers/l10n'; interface Props { organizations: Organization[]; } export default function OrganizationsList({ organizations }: Props) { + if (organizations.length === 0) { + return <div>{translate('my_account.organizations.no_results')}</div>; + } + return ( <ul className="account-projects-list"> {sortBy(organizations, organization => organization.name.toLocaleLowerCase()).map( diff --git a/server/sonar-web/src/main/js/apps/account/organizations/UserOrganizations.tsx b/server/sonar-web/src/main/js/apps/account/organizations/UserOrganizations.tsx index e6f34fbd8e2..d63555b1574 100644 --- a/server/sonar-web/src/main/js/apps/account/organizations/UserOrganizations.tsx +++ b/server/sonar-web/src/main/js/apps/account/organizations/UserOrganizations.tsx @@ -87,29 +87,22 @@ class UserOrganizations extends React.PureComponent<Props, State> { <div className="account-body account-container"> <Helmet title={translate('my_account.organizations')} /> - <header className="page-header"> - <h2 className="page-title">{translate('my_account.organizations')}</h2> + <div className="boxed-group"> {canCreateOrganizations && ( - <div className="page-actions"> - <button onClick={this.handleCreateClick}>{translate('create')}</button> + <div className="clearfix"> + <div className="boxed-group-actions"> + <button onClick={this.handleCreateClick}>{translate('create')}</button> + </div> </div> )} - {this.props.organizations.length > 0 ? ( - <div className="page-description"> - {translate('my_account.organizations.description')} - </div> - ) : ( - <div className="page-description"> - {translate('my_account.organizations.no_results')} - </div> - )} - </header> - - {this.state.loading ? ( - <i className="spinner" /> - ) : ( - <OrganizationsList organizations={this.props.organizations} /> - )} + <div className="boxed-group-inner"> + {this.state.loading ? ( + <i className="spinner" /> + ) : ( + <OrganizationsList organizations={this.props.organizations} /> + )} + </div> + </div> {this.state.createOrganization && ( <CreateOrganizationForm 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 index 4996bfcd124..464f549d5bd 100644 --- a/server/sonar-web/src/main/js/apps/account/profile/Profile.js +++ b/server/sonar-web/src/main/js/apps/account/profile/Profile.js @@ -45,29 +45,31 @@ function Profile(props /*: Props */) { return ( <div className="account-body account-container"> - <div className="spacer-bottom"> - {translate('login')}: <strong id="login">{user.login}</strong> - </div> + <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 id="identity-provider" className="spacer-bottom"> + <UserExternalIdentity user={user} /> + </div> + )} - {!user.local && - user.externalProvider !== 'sonarqube' && ( - <div id="identity-provider" className="spacer-bottom"> - <UserExternalIdentity user={user} /> + {!!user.email && ( + <div className="spacer-bottom"> + {translate('my_profile.email')}: <strong id="email">{user.email}</strong> </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} />} - {!customOrganizations && <hr className="account-separator" />} - {!customOrganizations && <UserGroups groups={user.groups} />} + <hr /> - <hr className="account-separator" /> - - <UserScmAccounts user={user} scmAccounts={user.scmAccounts} /> + <UserScmAccounts user={user} scmAccounts={user.scmAccounts} /> + </div> </div> ); } diff --git a/server/sonar-web/src/main/js/apps/account/templates/account-tokens.hbs b/server/sonar-web/src/main/js/apps/account/templates/account-tokens.hbs index 2c8da3e0297..65bbf91e160 100644 --- a/server/sonar-web/src/main/js/apps/account/templates/account-tokens.hbs +++ b/server/sonar-web/src/main/js/apps/account/templates/account-tokens.hbs @@ -1,77 +1,81 @@ -<h2 class="spacer-bottom">{{t 'users.tokens'}}</h2> +<div class="boxed-group"> + <h2>{{t 'users.tokens'}}</h2> -<div class="big-spacer-bottom big-spacer-right markdown"> - <p>{{t 'my_account.tokens_description'}}</p> -</div> +<div class="boxed-group-inner"> + <div class="big-spacer-bottom big-spacer-right markdown"> + <p>{{t 'my_account.tokens_description'}}</p> + </div> -{{#notNull tokens}} - <table class="data"> - <thead> - <tr> - <th>{{t 'name'}}</th> - <th class="text-right">{{t 'created'}}</th> - <th> </th> - </tr> - </thead> - <tbody> - {{#each tokens}} - <tr> - <td> - <div title="{{name}}"> - {{limitString name}} - </div> - </td> - <td class="thin nowrap text-right"> - {{d createdAt}} - </td> - <td class="thin nowrap text-right"> - <div class="big-spacer-left"> - <form class="js-revoke-token-form" data-token="{{name}}"> - {{#if deleting}} - <button class="button-red active input-small">{{t 'users.tokens.sure'}}</button> - {{else}} - <button class="button-red input-small">{{t 'users.tokens.revoke'}}</button> - {{/if}} - </form> - </div> - </td> - </tr> - {{else}} + {{#notNull tokens}} + <table class="data"> + <thead> <tr> - <td colspan="3"> - <span class="note">{{t 'users.no_tokens'}}</span> - </td> + <th>{{t 'name'}}</th> + <th class="text-right">{{t 'created'}}</th> + <th> </th> </tr> - {{/each}} - </tbody> - </table> -{{/notNull}} + </thead> + <tbody> + {{#each tokens}} + <tr> + <td> + <div title="{{name}}"> + {{limitString name}} + </div> + </td> + <td class="thin nowrap text-right"> + {{d createdAt}} + </td> + <td class="thin nowrap text-right"> + <div class="big-spacer-left"> + <form class="js-revoke-token-form" data-token="{{name}}"> + {{#if deleting}} + <button class="button-red active input-small">{{t 'users.tokens.sure'}}</button> + {{else}} + <button class="button-red input-small">{{t 'users.tokens.revoke'}}</button> + {{/if}} + </form> + </div> + </td> + </tr> + {{else}} + <tr> + <td colspan="3"> + <span class="note">{{t 'users.no_tokens'}}</span> + </td> + </tr> + {{/each}} + </tbody> + </table> + {{/notNull}} -{{#each errors}} - <div class="alert alert-danger">{{msg}}</div> -{{/each}} + {{#each errors}} + <div class="alert alert-danger">{{msg}}</div> + {{/each}} -<form class="js-generate-token-form spacer-top panel bg-muted"> - <label>{{t 'users.generate_new_token'}}:</label> - <input type="text" required maxlength="100" placeholder="{{t 'users.enter_token_name'}}"> - <button>{{t 'users.generate'}}</button> -</form> + <form class="js-generate-token-form spacer-top panel bg-muted"> + <label>{{t 'users.generate_new_token'}}:</label> + <input type="text" required maxlength="100" placeholder="{{t 'users.enter_token_name'}}"> + <button>{{t 'users.generate'}}</button> + </form> -{{#if newToken}} - <div class="panel panel-white big-spacer-top"> - <div class="alert alert-warning"> - {{tp 'users.tokens.new_token_created' newToken.name}} - </div> + {{#if newToken}} + <div class="panel panel-white big-spacer-top"> + <div class="alert alert-warning"> + {{tp 'users.tokens.new_token_created' newToken.name}} + </div> - <table class="data"> - <tr> - <td class="thin"> - <button class="js-copy-to-clipboard" data-clipboard-text="{{newToken.token}}">{{t 'copy'}}</button> - </td> - <td class="nowrap"> - <div class="monospaced text-success">{{newToken.token}}</div> - </td> - </tr> - </table> - </div> -{{/if}} + <table class="data"> + <tr> + <td class="thin"> + <button class="js-copy-to-clipboard" data-clipboard-text="{{newToken.token}}">{{t 'copy'}}</button> + </td> + <td class="nowrap"> + <div class="monospaced text-success">{{newToken.token}}</div> + </td> + </tr> + </table> + </div> + {{/if}} +</div> +</div> |