Skip to content

Commit

Permalink
Migrate Organization Settings to React (getredash#3728)
Browse files Browse the repository at this point in the history
* Migrate Organization Settings to React

* Fix failing spec and replace default values from inputs

* Add HelpTrigger and handleChange to SAML options

* Undo changes to ant-variables.less

* Add time format to OrganizationSettings
  • Loading branch information
gabrieldutra authored and harveyrendell committed Nov 14, 2019
1 parent 888723d commit 9feb66b
Show file tree
Hide file tree
Showing 6 changed files with 273 additions and 152 deletions.
4 changes: 4 additions & 0 deletions client/app/components/HelpTrigger.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,10 @@ export const TYPES = {
'/user-guide/dashboards/sharing-dashboards',
'Guide: Sharing and Embedding Dashboards',
],
AUTHENTICATION_OPTIONS: [
'/user-guide/users/authentication-options',
'Guide: Authentication Options',
],
DS_ATHENA: [
'/data-sources/amazon-athena-setup',
'Guide: Help Setting up Amazon Athena',
Expand Down
255 changes: 255 additions & 0 deletions client/app/pages/settings/OrganizationSettings.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,255 @@
import React from 'react';
import { react2angular } from 'react2angular';
import { isEmpty, join, get } from 'lodash';

import Alert from 'antd/lib/alert';
import Button from 'antd/lib/button';
import Form from 'antd/lib/form';
import Input from 'antd/lib/input';
import Select from 'antd/lib/select';
import Checkbox from 'antd/lib/checkbox';
import Tooltip from 'antd/lib/tooltip';
import LoadingState from '@/components/items-list/components/LoadingState';
import { HelpTrigger } from '@/components/HelpTrigger';

import { routesToAngularRoutes } from '@/lib/utils';
import { clientConfig } from '@/services/auth';
import settingsMenu from '@/services/settingsMenu';
import recordEvent from '@/services/recordEvent';
import OrgSettings from '@/services/organizationSettings';

const Option = Select.Option;

class OrganizationSettings extends React.Component {
state = {
settings: {},
formValues: {},
loading: true,
submitting: false,
};

componentDidMount() {
recordEvent('view', 'page', 'org_settings');
OrgSettings.get().then((response) => {
const settings = get(response, 'settings');
this.setState({ settings, formValues: { ...settings }, loading: false });
});
}

disablePasswordLoginToggle = () => !(clientConfig.googleLoginEnabled || this.state.formValues.auth_saml_enabled)

handleSubmit = (e) => {
e.preventDefault();
if (!this.state.submitting) {
this.setState({ submitting: true });
OrgSettings.save(this.state.formValues)
.then((response) => {
const settings = get(response, 'settings');
this.setState({ settings, formValues: { ...settings } });
})
.finally(() => this.setState({ submitting: false }));
}
};

handleChange = (name, value) => {
this.setState(prevState => ({ formValues: Object.assign(prevState.formValues, { [name]: value }) }), () => {
if (this.disablePasswordLoginToggle() && !this.state.formValues.auth_password_login_enabled) {
this.handleChange('auth_password_login_enabled', true);
}
});
};

renderGoogleLoginOptions() {
const { formValues } = this.state;
return (
<React.Fragment>
<h4>Google Login</h4>
<Form.Item label="Allowed Google Apps Domains">
<Select
mode="tags"
value={formValues.auth_google_apps_domains}
onChange={value => this.handleChange('auth_google_apps_domains', value)}
/>
{!isEmpty(formValues.auth_google_apps_domains) && (
<Alert
message={(
<p>
Any user registered with a <strong>{join(formValues.auth_google_apps_domains, ', ')}</strong>{' '}
Google Apps account will be able to login. If they don{'\''}t have an existing user,
a new user will be created and join the <strong>Default</strong> group.
</p>
)}
className="m-t-15"
/>
)}
</Form.Item>
</React.Fragment>
);
}

renderSAMLOptions() {
const { formValues } = this.state;
return (
<React.Fragment>
<h4>SAML</h4>
<Form.Item>
<Checkbox
name="auth_saml_enabled"
checked={formValues.auth_saml_enabled}
onChange={e => this.handleChange('auth_saml_enabled', e.target.checked)}
>
SAML Enabled
</Checkbox>
</Form.Item>
{formValues.auth_saml_enabled && (
<div>
<Form.Item label="SAML Metadata URL">
<Input
value={formValues.auth_saml_metadata_url}
onChange={e => this.handleChange('auth_saml_metadata_url', e.target.value)}
/>
</Form.Item>
<Form.Item label="SAML Entity ID">
<Input
value={formValues.auth_saml_entity_id}
onChange={e => this.handleChange('auth_saml_entity_id', e.target.value)}
/>
</Form.Item>
<Form.Item label="SAML NameID Format">
<Input
value={formValues.auth_saml_nameid_format}
onChange={e => this.handleChange('auth_saml_nameid_format', e.target.value)}
/>
</Form.Item>
</div>
)}
</React.Fragment>
);
}

renderGeneralSettings() {
const { formValues } = this.state;
return (
<React.Fragment>
<h3 className="m-t-0">General</h3>
<hr />
<Form.Item label="Date Format">
<Select
value={formValues.date_format}
onChange={value => this.handleChange('date_format', value)}
data-test="DateFormatSelect"
>
{clientConfig.dateFormatList.map(dateFormat => (
<Option key={dateFormat}>{dateFormat}</Option>
))}
</Select>
</Form.Item>
<Form.Item label="Time Format">
<Select
value={formValues.time_format}
onChange={value => this.handleChange('time_format', value)}
data-test="TimeFormatSelect"
>
{clientConfig.timeFormatList.map(timeFormat => (
<Option key={timeFormat}>{timeFormat}</Option>
))}
</Select>
</Form.Item>
<Form.Item label="Feature Flags">
<Checkbox
name="feature_show_permissions_control"
checked={formValues.feature_show_permissions_control}
onChange={e => this.handleChange('feature_show_permissions_control', e.target.checked)}
>
Enable experimental multiple owners support
</Checkbox>
</Form.Item>
</React.Fragment>
);
}

renderAuthSettings() {
const { settings, formValues } = this.state;
return (
<React.Fragment>
<h3 className="m-t-0">
Authentication <HelpTrigger type="AUTHENTICATION_OPTIONS" />
</h3>
<hr />
{!settings.auth_password_login_enabled && (
<Alert
message="Password based login is currently disabled and users will
be able to login only with the enabled SSO options."
type="warning"
className="m-t-15 m-b-15"
/>
)}
<Form.Item>
<Checkbox
checked={formValues.auth_password_login_enabled}
disabled={this.disablePasswordLoginToggle()}
onChange={e => this.handleChange('auth_password_login_enabled', e.target.checked)}
>
<Tooltip
title={this.disablePasswordLoginToggle() ?
'Password login can be disabled only if another login method is enabled.' : null}
placement="right"
>
Password Login Enabled
</Tooltip>
</Checkbox>
</Form.Item>
{clientConfig.googleLoginEnabled && this.renderGoogleLoginOptions()}
{this.renderSAMLOptions()}
</React.Fragment>
);
}

render() {
const { loading, submitting } = this.state;
return (
<div className="row" data-test="OrganizationSettings">
<div className="col-md-offset-4 col-md-4">
{loading ? (<LoadingState className="" />) : (
<Form layout="vertical" onSubmit={this.handleSubmit}>
{this.renderGeneralSettings()}
{this.renderAuthSettings()}
<Button className="w-100" type="primary" htmlType="submit" loading={submitting}>
Save
</Button>
</Form>
)}
</div>
</div>
);
}
}

export default function init(ngModule) {
settingsMenu.add({
permission: 'admin',
title: 'Settings',
path: 'settings/organization',
order: 6,
});

ngModule.component('pageOrganizationSettings', react2angular(OrganizationSettings));

return routesToAngularRoutes([
{
path: '/settings/organization',
title: 'Organization Settings',
key: 'organization-settings',
},
], {
reloadOnSearch: false,
template: '<settings-screen><page-organization-settings on-error="handleError"></page-organization-settings></settings-screen>',
controller($scope, $exceptionHandler) {
'ngInject';

$scope.handleError = $exceptionHandler;
},
});
}

init.init = true;
94 changes: 0 additions & 94 deletions client/app/pages/settings/organization.html

This file was deleted.

Loading

0 comments on commit 9feb66b

Please sign in to comment.