1
1
mirror of https://github.com/pterodactyl/panel.git synced 2024-11-23 01:22:30 +01:00

Begin working on password reset page

This commit is contained in:
Dane Everitt 2019-06-11 22:02:18 -07:00
parent d9f30294de
commit b93b40ba31
No known key found for this signature in database
GPG Key ID: EEA66103B3D71F53
8 changed files with 120 additions and 11 deletions

View File

@ -16,4 +16,13 @@
@apply .opacity-0; @apply .opacity-0;
transition: opacity 150ms; transition: opacity 150ms;
} }
/** @todo fix this, hides footer stuff */
div.route-transition-group {
@apply .relative;
& section {
@apply .absolute .w-full .pin-t .pin-l;
}
}
/*! purgecss end ignore */ /*! purgecss end ignore */

View File

@ -0,0 +1,9 @@
import http from '@/api/http';
export default (email: string): Promise<void> => {
return new Promise((resolve, reject) => {
http.post('/auth/password', { email })
.then(() => resolve())
.catch(reject);
});
};

View File

@ -9,6 +9,7 @@ const http: AxiosInstance = axios.create({
'X-Requested-With': 'XMLHttpRequest', 'X-Requested-With': 'XMLHttpRequest',
'Accept': 'application/json', 'Accept': 'application/json',
'Content-Type': 'application/json', 'Content-Type': 'application/json',
'X-CSRF-Token': (window as any).X_CSRF_TOKEN as string || '',
}, },
}); });

View File

@ -0,0 +1,75 @@
import * as React from 'react';
import OpenInputField from '@/components/forms/OpenInputField';
import { Link } from 'react-router-dom';
import requestPasswordResetEmail from '@/api/auth/requestPasswordResetEmail';
type Props = Readonly<{
}>;
type State = Readonly<{
email: string;
isSubmitting: boolean;
}>;
export default class ForgotPasswordContainer extends React.PureComponent<Props, State> {
state: State = {
email: '',
isSubmitting: false,
};
handleFieldUpdate = (e: React.ChangeEvent<HTMLInputElement>) => this.setState({
email: e.target.value,
});
handleSubmission = (e: React.FormEvent<HTMLFormElement>) => this.setState({ isSubmitting: true }, () => {
e.preventDefault();
requestPasswordResetEmail(this.state.email)
.then(() => {
})
.catch(console.error)
.then(() => this.setState({ isSubmitting: false }));
});
render () {
return (
<React.Fragment>
<form className={'login-box'} onSubmit={this.handleSubmission}>
<div className={'-mx-3'}>
<OpenInputField
id={'email'}
type={'email'}
label={'Email'}
description={'Enter your account email address to receive instructions on resetting your password.'}
autoFocus={true}
required={true}
onChange={this.handleFieldUpdate}
/>
</div>
<div className={'mt-6'}>
<button
className={'btn btn-primary btn-jumbo'}
disabled={this.state.isSubmitting || this.state.email.length < 5}
>
{this.state.isSubmitting ?
<span className={'spinner white'}>&nbsp;</span>
:
'Send Email'
}
</button>
</div>
<div className={'mt-6 text-center'}>
<Link
to={'/login'}
className={'text-xs text-neutral-500 tracking-wide uppercase no-underline hover:text-neutral-700'}
>
Return to Login
</Link>
</div>
</form>
</React.Fragment>
);
}
}

View File

@ -98,7 +98,7 @@ export default class LoginContainer extends React.PureComponent<{}, State> {
</div> </div>
<div className={'mt-6 text-center'}> <div className={'mt-6 text-center'}>
<Link <Link
to={'/auth/forgot-password'} to={'/forgot-password'}
className={'text-xs text-neutral-500 tracking-wide no-underline uppercase hover:text-neutral-600'} className={'text-xs text-neutral-500 tracking-wide no-underline uppercase hover:text-neutral-600'}
> >
Forgot password? Forgot password?

View File

@ -3,9 +3,10 @@ import classNames from 'classnames';
type Props = React.InputHTMLAttributes<HTMLInputElement> & { type Props = React.InputHTMLAttributes<HTMLInputElement> & {
label: string; label: string;
description?: string;
}; };
export default ({ className, onChange, label, ...props }: Props) => { export default ({ className, description, onChange, label, ...props }: Props) => {
const [ value, setValue ] = React.useState(''); const [ value, setValue ] = React.useState('');
const classes = classNames('input open-label', { const classes = classNames('input open-label', {
@ -25,6 +26,11 @@ export default ({ className, onChange, label, ...props }: Props) => {
{...props} {...props}
/> />
<label htmlFor={props.id}>{label}</label> <label htmlFor={props.id}>{label}</label>
{description &&
<p className={'text-xs text-neutral-500'}>
{description}
</p>
}
</div> </div>
); );
}; };

View File

@ -2,6 +2,7 @@ import * as React from 'react';
import { BrowserRouter, Route, Switch } from 'react-router-dom'; import { BrowserRouter, Route, Switch } from 'react-router-dom';
import LoginContainer from '@/components/auth/LoginContainer'; import LoginContainer from '@/components/auth/LoginContainer';
import { CSSTransition, TransitionGroup } from 'react-transition-group'; import { CSSTransition, TransitionGroup } from 'react-transition-group';
import ForgotPasswordContainer from '@/components/auth/ForgotPasswordContainer';
export default class AuthenticationRouter extends React.PureComponent { export default class AuthenticationRouter extends React.PureComponent {
render () { render () {
@ -9,13 +10,24 @@ export default class AuthenticationRouter extends React.PureComponent {
<BrowserRouter basename={'/auth'}> <BrowserRouter basename={'/auth'}>
<Route <Route
render={({ location }) => ( render={({ location }) => (
<TransitionGroup> <TransitionGroup className={'route-transition-group'}>
<CSSTransition key={location.key} timeout={150} classNames={'fade'}> <CSSTransition key={location.key} timeout={150} classNames={'fade'}>
<Switch location={location}> <section>
<Route path={'/login'} component={LoginContainer}/> <Switch location={location}>
<Route path={'/forgot-password'}/> <Route path={'/login'} component={LoginContainer}/>
<Route path={'/checkpoint'}/> <Route path={'/forgot-password'} component={ForgotPasswordContainer}/>
</Switch> <Route path={'/checkpoint'}/>
</Switch>
<p className={'text-center text-neutral-500 text-xs'}>
&copy; 2015 - 2019&nbsp;
<a
href={'https://pterodactyl.io'}
className={'no-underline text-neutral-500 hover:text-neutral-300'}
>
Pterodactyl Software
</a>
</p>
</section>
</CSSTransition> </CSSTransition>
</TransitionGroup> </TransitionGroup>
)} )}

View File

@ -5,8 +5,5 @@
@section('container') @section('container')
<div class="w-full max-w-xs sm:max-w-sm m-auto mt-8"> <div class="w-full max-w-xs sm:max-w-sm m-auto mt-8">
<div id="app"></div> <div id="app"></div>
<p class="text-center text-neutral-500 text-xs">
{!! trans('strings.copyright', ['year' => date('Y')]) !!}
</p>
</div> </div>
@endsection @endsection