change(ui) - login changes
This commit is contained in:
parent
561f333870
commit
7adf87a323
9 changed files with 294 additions and 240 deletions
|
|
@ -14,21 +14,23 @@ const LOGIN = loginRoute();
|
|||
const recaptchaRef = React.createRef();
|
||||
const ERROR_DONT_MATCH = "Passwords don't match.";
|
||||
const MIN_LENGTH = 8;
|
||||
const PASSWORD_POLICY = `Password should contain at least ${ MIN_LENGTH } symbols.`;
|
||||
const PASSWORD_POLICY = `Password should contain at least ${MIN_LENGTH} symbols.`;
|
||||
|
||||
const checkDontMatch = (newPassword, newPasswordRepeat) =>
|
||||
newPasswordRepeat.length > 0 && newPasswordRepeat !== newPassword;
|
||||
|
||||
@connect(
|
||||
(state, props) => ({
|
||||
errors: state.getIn([ 'user', 'requestResetPassowrd', 'errors' ]),
|
||||
resetErrors: state.getIn([ 'user', 'resetPassword', 'errors' ]),
|
||||
loading: state.getIn([ 'user', 'requestResetPassowrd', 'loading' ]) || state.getIn([ 'user', 'resetPassword', 'loading' ]),
|
||||
params: new URLSearchParams(props.location.search)
|
||||
errors: state.getIn(['user', 'requestResetPassowrd', 'errors']),
|
||||
resetErrors: state.getIn(['user', 'resetPassword', 'errors']),
|
||||
loading:
|
||||
state.getIn(['user', 'requestResetPassowrd', 'loading']) ||
|
||||
state.getIn(['user', 'resetPassword', 'loading']),
|
||||
params: new URLSearchParams(props.location.search),
|
||||
}),
|
||||
{ requestResetPassword, resetPassword, resetErrors },
|
||||
{ requestResetPassword, resetPassword, resetErrors }
|
||||
)
|
||||
@withPageTitle("Password Reset - OpenReplay")
|
||||
@withPageTitle('Password Reset - OpenReplay')
|
||||
@withRouter
|
||||
export default class ForgotPassword extends React.PureComponent {
|
||||
state = {
|
||||
|
|
@ -45,12 +47,14 @@ export default class ForgotPassword extends React.PureComponent {
|
|||
const { email, password } = this.state;
|
||||
const { params } = this.props;
|
||||
|
||||
const pass = params.get('pass')
|
||||
const invitation = params.get('invitation')
|
||||
const resetting = pass && invitation
|
||||
const pass = params.get('pass');
|
||||
const invitation = params.get('invitation');
|
||||
const resetting = pass && invitation;
|
||||
|
||||
if (!resetting) {
|
||||
this.props.requestResetPassword({ email: email.trim(), 'g-recaptcha-response': token }).then(() => {
|
||||
this.props
|
||||
.requestResetPassword({ email: email.trim(), 'g-recaptcha-response': token })
|
||||
.then(() => {
|
||||
const { errors } = this.props;
|
||||
if (!errors) this.setState({ requested: true });
|
||||
});
|
||||
|
|
@ -61,16 +65,15 @@ export default class ForgotPassword extends React.PureComponent {
|
|||
if (!resetErrors) this.setState({ updated: true });
|
||||
});
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
isSubmitDisabled() {
|
||||
const { password, passwordRepeat } = this.state;
|
||||
if (password !== passwordRepeat ||
|
||||
password.length < MIN_LENGTH) return true;
|
||||
if (password !== passwordRepeat || password.length < MIN_LENGTH) return true;
|
||||
return false;
|
||||
}
|
||||
|
||||
write = ({ target: { value, name } }) => this.setState({ [ name ]: value })
|
||||
write = ({ target: { value, name } }) => this.setState({ [name]: value });
|
||||
|
||||
shouldShouwPolicy() {
|
||||
const { password } = this.state;
|
||||
|
|
@ -79,18 +82,18 @@ export default class ForgotPassword extends React.PureComponent {
|
|||
return true;
|
||||
}
|
||||
|
||||
onSubmit = e => {
|
||||
onSubmit = (e) => {
|
||||
e.preventDefault();
|
||||
const { CAPTCHA_ENABLED } = this.state;
|
||||
if (CAPTCHA_ENABLED && recaptchaRef.current) {
|
||||
recaptchaRef.current.execute()
|
||||
recaptchaRef.current.execute();
|
||||
} else if (!CAPTCHA_ENABLED) {
|
||||
this.handleSubmit();
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
componentWillUnmount() {
|
||||
this.props.resetErrors()
|
||||
this.props.resetErrors();
|
||||
}
|
||||
|
||||
render() {
|
||||
|
|
@ -99,134 +102,152 @@ export default class ForgotPassword extends React.PureComponent {
|
|||
const { requested, updated, password, passwordRepeat, email } = this.state;
|
||||
const dontMatch = checkDontMatch(password, passwordRepeat);
|
||||
|
||||
const pass = params.get('pass')
|
||||
const invitation = params.get('invitation')
|
||||
const resetting = pass && invitation
|
||||
const validEmail = validateEmail(email)
|
||||
const pass = params.get('pass');
|
||||
const invitation = params.get('invitation');
|
||||
const resetting = pass && invitation;
|
||||
const validEmail = validateEmail(email);
|
||||
|
||||
return (
|
||||
<div className="flex" style={{ height: '100vh'}}>
|
||||
<div className={cn("w-6/12", stl.left)}>
|
||||
<div className="px-6 pt-10">
|
||||
<img src="/assets/logo-white.svg" />
|
||||
<div className="flex items-center justify-center h-screen">
|
||||
<div className="flex flex-col items-center">
|
||||
<div className="m-10 ">
|
||||
<img src="/assets/logo.svg" width={200} />
|
||||
</div>
|
||||
<div className="color-white text-lg flex items-center">
|
||||
<div className="flex items-center justify-center w-full" style={{ height: 'calc(100vh - 130px)'}}>
|
||||
<div className="text-4xl">Welcome Back!</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div className="w-6/12 flex items-center justify-center">
|
||||
<Form onSubmit={ this.onSubmit } style={{ minWidth: '50%' }} className="flex flex-col items-center justify-center">
|
||||
<div className="mb-8">
|
||||
<h2 className="text-center text-3xl mb-6">{`${resetting ? 'Create' : 'Reset'} Password`}</h2>
|
||||
</div>
|
||||
<Loader loading={ loading }>
|
||||
<div data-hidden={ updated } className="w-full">
|
||||
{ CAPTCHA_ENABLED && (
|
||||
<div className={ stl.recaptcha }>
|
||||
<div className="border rounded bg-white" style={{ width: '350px' }}>
|
||||
<h2 className="text-center text-2xl font-medium mb-6 border-b p-5 w-full">
|
||||
{ resetting ? "Create Password" : "Reset Password" }
|
||||
</h2>
|
||||
|
||||
<div className="px-8">
|
||||
{resetting && <div className="my-6">Provide your email address, so we can send you a link to reset your password.</div> }
|
||||
<Form
|
||||
onSubmit={this.onSubmit}
|
||||
style={{ minWidth: '50%' }}
|
||||
className="flex flex-col items-center justify-center"
|
||||
>
|
||||
{/* <div className="mb-8">
|
||||
<h2 className="text-center text-3xl mb-6">{`${
|
||||
resetting ? 'Create' : 'Reset'
|
||||
} Password`}</h2>
|
||||
</div> */}
|
||||
<Loader loading={loading}>
|
||||
<div data-hidden={updated} className="w-full">
|
||||
{CAPTCHA_ENABLED && (
|
||||
<div className={stl.recaptcha}>
|
||||
<ReCAPTCHA
|
||||
ref={ recaptchaRef }
|
||||
ref={recaptchaRef}
|
||||
size="invisible"
|
||||
data-hidden={ requested }
|
||||
sitekey={ window.env.CAPTCHA_SITE_KEY }
|
||||
onChange={ token => this.handleSubmit(token) }
|
||||
data-hidden={requested}
|
||||
sitekey={window.env.CAPTCHA_SITE_KEY}
|
||||
onChange={(token) => this.handleSubmit(token)}
|
||||
/>
|
||||
</div>
|
||||
)}
|
||||
|
||||
{ !resetting && !requested &&
|
||||
{!resetting && !requested && (
|
||||
<Form.Field>
|
||||
<i className={ stl.inputIconUser } />
|
||||
<label>{'Email Address:'}</label>
|
||||
<Input
|
||||
autoFocus={true}
|
||||
autocomplete="email"
|
||||
type="text"
|
||||
placeholder="Email"
|
||||
name="email"
|
||||
onChange={ this.write }
|
||||
onChange={this.write}
|
||||
className="w-full"
|
||||
icon="user-alt"
|
||||
icon="envelope"
|
||||
/>
|
||||
</Form.Field>
|
||||
}
|
||||
)}
|
||||
|
||||
{
|
||||
requested && !errors && (
|
||||
<div>Reset password link has been sent to your email.</div>
|
||||
)
|
||||
}
|
||||
{requested && !errors && (
|
||||
<div className="flex flex-col items-center justify-center">
|
||||
<div className="w-16 h-16 rounded-full bg-tealx-light flex items-center justify-center mb-2">
|
||||
<Icon name="envelope-check" size={30} color="tealx" />
|
||||
</div>
|
||||
<div>Alright! a reset link was emailed to {email}. Click on it to reset your account password.</div>
|
||||
</div>
|
||||
)}
|
||||
|
||||
{
|
||||
resetting && (
|
||||
{resetting && (
|
||||
<React.Fragment>
|
||||
<Form.Field>
|
||||
<i className={ stl.inputIconPassword } />
|
||||
<i className={stl.inputIconPassword} />
|
||||
<Input
|
||||
autocomplete="new-password"
|
||||
type="password"
|
||||
placeholder="Password"
|
||||
name="password"
|
||||
onChange={ this.write }
|
||||
onChange={this.write}
|
||||
className="w-full"
|
||||
/>
|
||||
</Form.Field>
|
||||
<div className={ stl.passwordPolicy } data-hidden={ !this.shouldShouwPolicy() }>
|
||||
{ PASSWORD_POLICY }
|
||||
<div className={stl.passwordPolicy} data-hidden={!this.shouldShouwPolicy()}>
|
||||
{PASSWORD_POLICY}
|
||||
</div>
|
||||
<Form.Field>
|
||||
<i className={ stl.inputIconPassword } />
|
||||
<i className={stl.inputIconPassword} />
|
||||
<Input
|
||||
autocomplete="new-password"
|
||||
type="password"
|
||||
placeholder="Confirm Password"
|
||||
name="passwordRepeat"
|
||||
onChange={ this.write }
|
||||
onChange={this.write}
|
||||
className="w-full"
|
||||
/>
|
||||
</Form.Field>
|
||||
</React.Fragment>
|
||||
)
|
||||
}
|
||||
)}
|
||||
|
||||
<Message error hidden={ !dontMatch }>
|
||||
{ ERROR_DONT_MATCH }
|
||||
<Message error hidden={!dontMatch}>
|
||||
{ERROR_DONT_MATCH}
|
||||
</Message>
|
||||
</div>
|
||||
</Loader>
|
||||
<div className="mt-4">
|
||||
{ errors &&
|
||||
<div className={ stl.errors }>
|
||||
{ errors.map(error => <span>{ error }<br /></span>) }
|
||||
{errors && (
|
||||
<div className={stl.errors}>
|
||||
{errors.map((error) => (
|
||||
<span>
|
||||
{error}
|
||||
<br />
|
||||
</span>
|
||||
))}
|
||||
</div>
|
||||
}
|
||||
<div data-hidden={ !updated } className={ stl.success }>
|
||||
)}
|
||||
<div data-hidden={!updated} className={stl.success}>
|
||||
<Icon name="check" size="30" color="green" />
|
||||
{ 'Your password has been updated sucessfully.' }
|
||||
{'Your password has been updated sucessfully.'}
|
||||
</div>
|
||||
</div>
|
||||
{/* <div className={ stl.formFooter }> */}
|
||||
|
||||
{!(updated || requested) && (
|
||||
<Button
|
||||
type="submit"
|
||||
variant="primary"
|
||||
loading={loading}
|
||||
disabled={ (resetting && this.isSubmitDisabled()) || (!resetting && !validEmail)}
|
||||
className="w-full"
|
||||
disabled={(resetting && this.isSubmitDisabled()) || (!resetting && !validEmail)}
|
||||
>
|
||||
{ resetting ? 'Create' : 'Reset' }
|
||||
{resetting ? 'Create' : 'Email password reset link'}
|
||||
</Button>
|
||||
)}
|
||||
|
||||
<div className={ stl.links }>
|
||||
<Link to={ LOGIN }>
|
||||
{ updated && (<Button variant="primary" type="submit" primary >{ 'Login' }</Button>)}
|
||||
<div data-hidden={ updated }>{'Back to Login'}</div>
|
||||
<div className="my-8">
|
||||
<Link to={LOGIN}>
|
||||
{updated && (
|
||||
<Button variant="primary" type="submit" primary>
|
||||
{'Login'}
|
||||
</Button>
|
||||
)}
|
||||
<div data-hidden={updated} className="link">{'Back to Login'}</div>
|
||||
</Link>
|
||||
</div>
|
||||
{/* </div> */}
|
||||
</Form>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -69,18 +69,16 @@ class Login extends React.Component {
|
|||
const { CAPTCHA_ENABLED } = this.state;
|
||||
|
||||
return (
|
||||
<div className="flex flex-col md:flex-row" style={{ height: '100vh' }}>
|
||||
<div className={cn('md:w-6/12 relative overflow-hidden', stl.left)}>
|
||||
<div className="px-6 pt-10">
|
||||
<img src="/assets/logo-white.svg" />
|
||||
<div className="flex items-center justify-center h-screen">
|
||||
<div className="flex flex-col items-center">
|
||||
<div className="m-10 ">
|
||||
<img src="/assets/logo.svg" width={200}/>
|
||||
</div>
|
||||
<img style={{ width: '800px', position: 'absolute', bottom: -100, left: 0 }} src={LoginBg} />
|
||||
</div>
|
||||
<div className="md:w-6/12 flex items-center justify-center py-10">
|
||||
<div className="">
|
||||
<div className="border rounded bg-white">
|
||||
<Form onSubmit={this.onSubmit} className="flex items-center justify-center flex-col">
|
||||
<div className="mb-8">
|
||||
<h2 className="text-center text-3xl mb-6">Login to OpenReplay</h2>
|
||||
<h2 className="text-center text-2xl font-medium mb-6 border-b p-5 w-full">Login to your account</h2>
|
||||
<div className="">
|
||||
|
||||
{!authDetails.tenants && (
|
||||
<div className="text-center text-xl">
|
||||
Don't have an account?{' '}
|
||||
|
|
@ -99,19 +97,19 @@ class Login extends React.Component {
|
|||
onChange={(token) => this.handleSubmit(token)}
|
||||
/>
|
||||
)}
|
||||
<div style={{ width: '350px' }}>
|
||||
<div style={{ width: '350px' }} className="px-8">
|
||||
<div className="mb-6">
|
||||
<label>Email</label>
|
||||
<label>Email Address</label>
|
||||
<Input
|
||||
data-test-id={"login"}
|
||||
autoFocus={true}
|
||||
autoComplete="username"
|
||||
type="text"
|
||||
placeholder="Email"
|
||||
placeholder="e.g. john@example.com"
|
||||
name="email"
|
||||
onChange={this.write}
|
||||
required
|
||||
icon="user-alt"
|
||||
icon="envelope"
|
||||
/>
|
||||
</div>
|
||||
<div className="mb-6">
|
||||
|
|
@ -142,15 +140,16 @@ class Login extends React.Component {
|
|||
))}
|
||||
</div>
|
||||
) : null}
|
||||
{/* <div className={ stl.formFooter }> */}
|
||||
<Button data-test-id={"log-button"} className="mt-2" type="submit" variant="primary">
|
||||
|
||||
<div className="px-8 w-full">
|
||||
<Button data-test-id={"log-button"} className="mt-2 w-full text-center" type="submit" variant="primary">
|
||||
{'Login'}
|
||||
</Button>
|
||||
|
||||
<div className={cn(stl.links, 'text-lg')}>
|
||||
<Link to={FORGOT_PASSWORD}>{'Forgot your password?'}</Link>
|
||||
<div className="my-8">
|
||||
<span className="color-gray-medium">Having trouble logging in?</span> <Link to={FORGOT_PASSWORD} className="link ml-1">{'Reset password'}</Link>
|
||||
</div>
|
||||
</div>
|
||||
{/* </div> */}
|
||||
</Form>
|
||||
|
||||
<div className={cn(stl.sso, 'py-2 flex flex-col items-center')}>
|
||||
|
|
@ -182,6 +181,8 @@ class Login extends React.Component {
|
|||
</Tooltip>
|
||||
)}
|
||||
</div>
|
||||
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
|
|
|||
|
|
@ -19,24 +19,19 @@ const BulletItem = ({ text }) => (
|
|||
export default class Signup extends React.Component {
|
||||
render() {
|
||||
return (
|
||||
<div className="flex" style={{ height: '100vh' }}>
|
||||
<div className={cn('w-6/12 relative overflow-hidden', stl.left)}>
|
||||
<div className="px-6 pt-10">
|
||||
<img src="/assets/logo-white.svg" />
|
||||
</div>
|
||||
<img
|
||||
style={{ width: '800px', position: 'absolute', bottom: -100, left: 0 }}
|
||||
src={RegisterBg}
|
||||
/>
|
||||
<div className="color-white text-lg flex items-center px-20 pt-32">
|
||||
<div className="flex justify-center items-center gap-6" style={{ height: '100vh' }}>
|
||||
<div className={cn('relative overflow-hidden')}>
|
||||
<div className="text-lg flex items-center" style={{ width: '350px'}}>
|
||||
<div>
|
||||
<div className="flex items-center text-3xl font-bold mb-6">
|
||||
OpenReplay Cloud{' '}
|
||||
<div className="ml-2">
|
||||
<Icon name="signup" size="28" color="white" />
|
||||
<div className="flex items-end text-3xl font-bold mb-6">
|
||||
<div className="">
|
||||
<img src="/assets/logo.svg" width={200}/>
|
||||
</div>{' '}
|
||||
<div className="ml-2 text-lg color-gray-medium">
|
||||
Cloud
|
||||
</div>
|
||||
</div>
|
||||
<div>OpenReplay Cloud is the hosted version of our open-source project.</div>
|
||||
<div className="border-b pb-2 mb-2">OpenReplay Cloud is the hosted version of our <a className="link" href="https://github.com/openreplay/openreplay" target="_blank">open-source</a> project.</div>
|
||||
<div>We’ll manage hosting, scaling and upgrades.</div>
|
||||
|
||||
<div className="mt-8">
|
||||
|
|
@ -47,7 +42,7 @@ export default class Signup extends React.Component {
|
|||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div className="w-6/12 flex items-center justify-center">
|
||||
<div className="flex items-center justify-center">
|
||||
<div className="">
|
||||
<SignupForm />
|
||||
</div>
|
||||
|
|
|
|||
|
|
@ -1,26 +1,25 @@
|
|||
import React from 'react'
|
||||
import { Form, Input, Icon, Button, Link } from 'UI'
|
||||
import { login } from 'App/routes'
|
||||
import ReCAPTCHA from 'react-google-recaptcha'
|
||||
import stl from './signup.module.css'
|
||||
import React from 'react';
|
||||
import { Form, Input, Icon, Button, Link } from 'UI';
|
||||
import { login } from 'App/routes';
|
||||
import ReCAPTCHA from 'react-google-recaptcha';
|
||||
import stl from './signup.module.css';
|
||||
import { signup } from 'Duck/user';
|
||||
import { connect } from 'react-redux'
|
||||
import Select from 'Shared/Select'
|
||||
import { connect } from 'react-redux';
|
||||
import Select from 'Shared/Select';
|
||||
import { SITE_ID_STORAGE_KEY } from 'App/constants/storageKeys';
|
||||
|
||||
const LOGIN_ROUTE = login()
|
||||
const recaptchaRef = React.createRef()
|
||||
const LOGIN_ROUTE = login();
|
||||
const recaptchaRef = React.createRef();
|
||||
|
||||
@connect(
|
||||
state => ({
|
||||
(state) => ({
|
||||
tenants: state.getIn(['user', 'tenants']),
|
||||
errors: state.getIn([ 'user', 'signupRequest', 'errors' ]),
|
||||
loading: state.getIn([ 'user', 'signupRequest', 'loading' ]),
|
||||
errors: state.getIn(['user', 'signupRequest', 'errors']),
|
||||
loading: state.getIn(['user', 'signupRequest', 'loading']),
|
||||
}),
|
||||
{ signup },
|
||||
{ signup }
|
||||
)
|
||||
export default class SignupForm extends React.Component {
|
||||
|
||||
state = {
|
||||
tenantId: '',
|
||||
fullname: '',
|
||||
|
|
@ -36,21 +35,30 @@ export default class SignupForm extends React.Component {
|
|||
if (props.errors && props.errors.size > 0 && state.reload) {
|
||||
recaptchaRef.current.reset();
|
||||
return {
|
||||
reload: false
|
||||
}
|
||||
reload: false,
|
||||
};
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
handleSubmit = (token) => {
|
||||
const { tenantId, fullname, password, email, projectName, organizationName, auth } = this.state;
|
||||
localStorage.removeItem(SITE_ID_STORAGE_KEY)
|
||||
this.props.signup({ tenantId, fullname, password, email, projectName, organizationName, auth, 'g-recaptcha-response': token })
|
||||
this.setState({ reload: true })
|
||||
}
|
||||
localStorage.removeItem(SITE_ID_STORAGE_KEY);
|
||||
this.props.signup({
|
||||
tenantId,
|
||||
fullname,
|
||||
password,
|
||||
email,
|
||||
projectName,
|
||||
organizationName,
|
||||
auth,
|
||||
'g-recaptcha-response': token,
|
||||
});
|
||||
this.setState({ reload: true });
|
||||
};
|
||||
|
||||
write = ({ target: { value, name } }) => this.setState({ [ name ]: value })
|
||||
writeOption = ({ name, value }) => this.setState({ [ name ]: value.value });
|
||||
write = ({ target: { value, name } }) => this.setState({ [name]: value });
|
||||
writeOption = ({ name, value }) => this.setState({ [name]: value.value });
|
||||
|
||||
onSubmit = (e) => {
|
||||
e.preventDefault();
|
||||
|
|
@ -60,52 +68,51 @@ export default class SignupForm extends React.Component {
|
|||
} else if (!CAPTCHA_ENABLED) {
|
||||
this.handleSubmit();
|
||||
}
|
||||
}
|
||||
};
|
||||
render() {
|
||||
const { loading, errors, tenants } = this.props;
|
||||
const { CAPTCHA_ENABLED } = this.state;
|
||||
|
||||
return (
|
||||
<Form onSubmit={ this.onSubmit }>
|
||||
<Form onSubmit={this.onSubmit} className="bg-white border rounded">
|
||||
<div className="mb-8">
|
||||
<h2 className="text-center text-3xl mb-6">Get Started</h2>
|
||||
<div className="text-center text-xl">Already having an account? <span className="link"><Link to={ LOGIN_ROUTE }>Sign in</Link></span></div>
|
||||
<h2 className="text-center text-2xl font-medium mb-6 border-b p-5 w-full">Create Account</h2>
|
||||
</div>
|
||||
<>
|
||||
{ CAPTCHA_ENABLED && (
|
||||
{CAPTCHA_ENABLED && (
|
||||
<ReCAPTCHA
|
||||
ref={ recaptchaRef }
|
||||
ref={recaptchaRef}
|
||||
size="invisible"
|
||||
sitekey={ window.env.CAPTCHA_SITE_KEY }
|
||||
onChange={ token => this.handleSubmit(token) }
|
||||
sitekey={window.env.CAPTCHA_SITE_KEY}
|
||||
onChange={(token) => this.handleSubmit(token)}
|
||||
/>
|
||||
)}
|
||||
<div>
|
||||
{ tenants.length > 0 && (
|
||||
<div className="px-8">
|
||||
{tenants.length > 0 && (
|
||||
<Form.Field>
|
||||
<label>Existing Accounts</label>
|
||||
<Select
|
||||
className="w-full"
|
||||
placeholder="Select account"
|
||||
selection
|
||||
options={ tenants }
|
||||
options={tenants}
|
||||
name="tenantId"
|
||||
// value={ instance.currentPeriod }
|
||||
onChange={ this.writeOption }
|
||||
onChange={this.writeOption}
|
||||
/>
|
||||
</Form.Field>
|
||||
)}
|
||||
<Form.Field>
|
||||
<label>Email</label>
|
||||
<label>Email Address</label>
|
||||
<Input
|
||||
autoFocus={true}
|
||||
autoComplete="username"
|
||||
type="email"
|
||||
placeholder="E.g. email@yourcompany.com"
|
||||
name="email"
|
||||
onChange={ this.write }
|
||||
className={ stl.email }
|
||||
onChange={this.write}
|
||||
required="true"
|
||||
icon="envelope"
|
||||
/>
|
||||
</Form.Field>
|
||||
<Form.Field>
|
||||
|
|
@ -115,9 +122,9 @@ export default class SignupForm extends React.Component {
|
|||
placeholder="Min 8 Characters"
|
||||
minLength="8"
|
||||
name="password"
|
||||
onChange={ this.write }
|
||||
className={ stl.password }
|
||||
onChange={this.write}
|
||||
required="true"
|
||||
icon="key"
|
||||
/>
|
||||
</Form.Field>
|
||||
<Form.Field>
|
||||
|
|
@ -126,46 +133,61 @@ export default class SignupForm extends React.Component {
|
|||
type="text"
|
||||
placeholder="E.g John Doe"
|
||||
name="fullname"
|
||||
onChange={ this.write }
|
||||
className={ stl.email }
|
||||
onChange={this.write}
|
||||
required="true"
|
||||
icon="user-alt"
|
||||
/>
|
||||
</Form.Field>
|
||||
|
||||
<Form.Field>
|
||||
<label>Organization</label>
|
||||
<Input
|
||||
type="text"
|
||||
placeholder="E.g Uber"
|
||||
name="organizationName"
|
||||
onChange={ this.write }
|
||||
className={ stl.email }
|
||||
onChange={this.write}
|
||||
required="true"
|
||||
icon="buildings"
|
||||
/>
|
||||
</Form.Field>
|
||||
|
||||
<div className="mb-6">
|
||||
<div className="text-sm">By creating an account, you agree to our <a href="https://openreplay.com/terms.html" className="link">Terms of Service</a> and <a href="https://openreplay.com/privacy.html" className="link">Privacy Policy</a>.</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</>
|
||||
{ errors &&
|
||||
<div className={ stl.errors }>
|
||||
{ errors.map(error => (
|
||||
<div className={stl.errorItem}>
|
||||
<Icon name="info" color="red" size="20"/>
|
||||
<span className="color-red ml-2">{ error }<br /></span>
|
||||
</div>
|
||||
)) }
|
||||
</div>
|
||||
}
|
||||
<div className={ stl.formFooter }>
|
||||
<Button type="submit" variant="primary" loading={loading}>
|
||||
<Button type="submit" variant="primary" loading={loading} className="w-full">
|
||||
Create account
|
||||
</Button>
|
||||
<div className="my-6">
|
||||
<div className="text-sm">
|
||||
By signing up, you agree to our{' '}
|
||||
<a href="https://openreplay.com/terms.html" className="link">
|
||||
terms of service
|
||||
</a>{' '}
|
||||
and{' '}
|
||||
<a href="https://openreplay.com/privacy.html" className="link">
|
||||
privacy policy
|
||||
</a>
|
||||
.
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</>
|
||||
{errors && (
|
||||
<div className={stl.errors}>
|
||||
{errors.map((error) => (
|
||||
<div className={stl.errorItem}>
|
||||
<Icon name="info" color="red" size="20" />
|
||||
<span className="color-red ml-2">
|
||||
{error}
|
||||
<br />
|
||||
</span>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
)}
|
||||
<div className="text-center bg-gray-50 py-4 border-t">
|
||||
Already having an account?{' '}
|
||||
<span className="link">
|
||||
<Link to={LOGIN_ROUTE}>Login</Link>
|
||||
</span>
|
||||
</div>
|
||||
</Form>
|
||||
)
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
@ -60,7 +60,7 @@ export default (props: Props) => {
|
|||
}
|
||||
|
||||
const render = () => (
|
||||
<button {...rest} type={type} className={cn(classes, className)}>
|
||||
<button {...rest} type={type} className={cn(classes, className, 'flex items-center justify-center')}>
|
||||
{icon && (
|
||||
// @ts-ignore
|
||||
<Icon className={cn({ 'mr-2': children })} name={icon} color={iconColor} size={iconSize} />
|
||||
|
|
|
|||
File diff suppressed because one or more lines are too long
4
frontend/app/svg/icons/buildings.svg
Normal file
4
frontend/app/svg/icons/buildings.svg
Normal file
|
|
@ -0,0 +1,4 @@
|
|||
<svg xmlns="http://www.w3.org/2000/svg" fill="currentColor" class="bi bi-buildings" viewBox="0 0 16 16">
|
||||
<path d="M14.763.075A.5.5 0 0 1 15 .5v15a.5.5 0 0 1-.5.5h-3a.5.5 0 0 1-.5-.5V14h-1v1.5a.5.5 0 0 1-.5.5h-9a.5.5 0 0 1-.5-.5V10a.5.5 0 0 1 .342-.474L6 7.64V4.5a.5.5 0 0 1 .276-.447l8-4a.5.5 0 0 1 .487.022ZM6 8.694 1 10.36V15h5V8.694ZM7 15h2v-1.5a.5.5 0 0 1 .5-.5h2a.5.5 0 0 1 .5.5V15h2V1.309l-7 3.5V15Z"/>
|
||||
<path d="M2 11h1v1H2v-1Zm2 0h1v1H4v-1Zm-2 2h1v1H2v-1Zm2 0h1v1H4v-1Zm4-4h1v1H8V9Zm2 0h1v1h-1V9Zm-2 2h1v1H8v-1Zm2 0h1v1h-1v-1Zm2-2h1v1h-1V9Zm0 2h1v1h-1v-1ZM8 7h1v1H8V7Zm2 0h1v1h-1V7Zm2 0h1v1h-1V7ZM8 5h1v1H8V5Zm2 0h1v1h-1V5Zm2 0h1v1h-1V5Zm0-2h1v1h-1V3Z"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 672 B |
4
frontend/app/svg/icons/envelope-check.svg
Normal file
4
frontend/app/svg/icons/envelope-check.svg
Normal file
|
|
@ -0,0 +1,4 @@
|
|||
<svg xmlns="http://www.w3.org/2000/svg" fill="currentColor" class="bi bi-envelope-check" viewBox="0 0 16 16">
|
||||
<path d="M2 2a2 2 0 0 0-2 2v8.01A2 2 0 0 0 2 14h5.5a.5.5 0 0 0 0-1H2a1 1 0 0 1-.966-.741l5.64-3.471L8 9.583l7-4.2V8.5a.5.5 0 0 0 1 0V4a2 2 0 0 0-2-2H2Zm3.708 6.208L1 11.105V5.383l4.708 2.825ZM1 4.217V4a1 1 0 0 1 1-1h12a1 1 0 0 1 1 1v.217l-7 4.2-7-4.2Z"/>
|
||||
<path d="M16 12.5a3.5 3.5 0 1 1-7 0 3.5 3.5 0 0 1 7 0Zm-1.993-1.679a.5.5 0 0 0-.686.172l-1.17 1.95-.547-.547a.5.5 0 0 0-.708.708l.774.773a.75.75 0 0 0 1.174-.144l1.335-2.226a.5.5 0 0 0-.172-.686Z"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 574 B |
4
frontend/app/svg/icons/key.svg
Normal file
4
frontend/app/svg/icons/key.svg
Normal file
|
|
@ -0,0 +1,4 @@
|
|||
<svg xmlns="http://www.w3.org/2000/svg" fill="currentColor" class="bi bi-key" viewBox="0 0 16 16">
|
||||
<path d="M0 8a4 4 0 0 1 7.465-2H14a.5.5 0 0 1 .354.146l1.5 1.5a.5.5 0 0 1 0 .708l-1.5 1.5a.5.5 0 0 1-.708 0L13 9.207l-.646.647a.5.5 0 0 1-.708 0L11 9.207l-.646.647a.5.5 0 0 1-.708 0L9 9.207l-.646.647A.5.5 0 0 1 8 10h-.535A4 4 0 0 1 0 8zm4-3a3 3 0 1 0 2.712 4.285A.5.5 0 0 1 7.163 9h.63l.853-.854a.5.5 0 0 1 .708 0l.646.647.646-.647a.5.5 0 0 1 .708 0l.646.647.646-.647a.5.5 0 0 1 .708 0l.646.647.793-.793-1-1h-6.63a.5.5 0 0 1-.451-.285A3 3 0 0 0 4 5z"/>
|
||||
<path d="M4 8a1 1 0 1 1-2 0 1 1 0 0 1 2 0z"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 608 B |
Loading…
Add table
Reference in a new issue