mirror of
https://github.com/elyby/accounts-frontend.git
synced 2024-12-03 20:19:07 +05:30
347 lines
12 KiB
TypeScript
347 lines
12 KiB
TypeScript
import React, { ReactNode } from 'react';
|
||
import { defineMessages, FormattedMessage as Message } from 'react-intl';
|
||
import { Helmet } from 'react-helmet-async';
|
||
|
||
import { SlideMotion } from 'app/components/ui/motion';
|
||
import { ScrollIntoView } from 'app/components/ui/scroll';
|
||
import { Input, Button, Form, FormModel, FormError } from 'app/components/ui/form';
|
||
import { BackButton } from 'app/components/profile/ProfileForm';
|
||
import styles from 'app/components/profile/profileForm.scss';
|
||
import helpLinks from 'app/components/auth/helpLinks.scss';
|
||
import Stepper from 'app/components/ui/stepper';
|
||
|
||
import changeEmail from './changeEmail.scss';
|
||
|
||
const STEPS_TOTAL = 3;
|
||
|
||
export type ChangeEmailStep = 0 | 1 | 2;
|
||
|
||
interface Props {
|
||
onChangeStep: (step: ChangeEmailStep) => void;
|
||
email: string;
|
||
stepForms: Array<FormModel>;
|
||
onSubmit: (step: ChangeEmailStep, form: FormModel) => Promise<void>;
|
||
step: ChangeEmailStep;
|
||
code?: string;
|
||
}
|
||
|
||
interface State {
|
||
newEmail: string | null;
|
||
activeStep: ChangeEmailStep;
|
||
code: string;
|
||
}
|
||
|
||
interface FormStepParams {
|
||
form: FormModel;
|
||
isActiveStep: boolean;
|
||
isCodeSpecified: boolean;
|
||
email: string;
|
||
code?: string;
|
||
}
|
||
|
||
const labels = defineMessages({
|
||
codePlaceholder: 'Paste the code here',
|
||
newEmailPlaceholder: 'Enter new E‑mail',
|
||
});
|
||
|
||
export default class ChangeEmail extends React.Component<Props, State> {
|
||
static get defaultProps(): Partial<Props> {
|
||
return {
|
||
stepForms: [new FormModel(), new FormModel(), new FormModel()],
|
||
onChangeStep() {},
|
||
step: 0,
|
||
};
|
||
}
|
||
|
||
state: State = {
|
||
newEmail: null,
|
||
activeStep: this.props.step,
|
||
code: this.props.code || '',
|
||
};
|
||
|
||
static getDerivedStateFromProps(props: Props, state: State) {
|
||
return {
|
||
activeStep: typeof props.step === 'number' ? props.step : state.activeStep,
|
||
code: props.code || state.code,
|
||
};
|
||
}
|
||
|
||
render() {
|
||
const { activeStep } = this.state;
|
||
const form = this.props.stepForms[activeStep];
|
||
|
||
return (
|
||
<Form form={form} onSubmit={this.onFormSubmit} onInvalid={() => this.forceUpdate()}>
|
||
<div className={styles.contentWithBackButton} data-testid="change-email">
|
||
<BackButton />
|
||
|
||
<div className={styles.form}>
|
||
<div className={styles.formBody}>
|
||
<Message key="changeEmailTitle" defaultMessage="Change E‑mail">
|
||
{(pageTitle) => (
|
||
<h3 className={styles.violetTitle}>
|
||
<Helmet title={pageTitle as string} />
|
||
{pageTitle}
|
||
</h3>
|
||
)}
|
||
</Message>
|
||
|
||
<div className={styles.formRow}>
|
||
<p className={styles.description}>
|
||
<Message
|
||
key="changeEmailDescription"
|
||
defaultMessage="To change current account E‑mail you must first verify that you own the current address and then confirm the new one."
|
||
/>
|
||
</p>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<div className={styles.stepper}>
|
||
<Stepper color="violet" totalSteps={STEPS_TOTAL} activeStep={activeStep} />
|
||
</div>
|
||
|
||
<div className={styles.form}>
|
||
{activeStep > 0 ? <ScrollIntoView /> : null}
|
||
|
||
{this.renderStepForms()}
|
||
|
||
<Button color="violet" type="submit" block>
|
||
{this.isLastStep() ? (
|
||
<Message key="changeEmailButton" defaultMessage="Change E‑mail" />
|
||
) : (
|
||
<Message key="sendEmailButton" defaultMessage="Send E‑mail" />
|
||
)}
|
||
</Button>
|
||
</div>
|
||
|
||
<div className={helpLinks.helpLinks}>
|
||
{this.isLastStep() ? null : (
|
||
<a href="#" onClick={this.onSwitchStep}>
|
||
<Message key="alreadyReceivedCode" defaultMessage="Already received code" />
|
||
</a>
|
||
)}
|
||
</div>
|
||
</div>
|
||
</Form>
|
||
);
|
||
}
|
||
|
||
renderStepForms() {
|
||
const { email } = this.props;
|
||
const { activeStep, code } = this.state;
|
||
const isCodeSpecified = !!this.props.code;
|
||
|
||
return (
|
||
<SlideMotion activeStep={activeStep}>
|
||
{new Array(STEPS_TOTAL).fill(0).map((_, step) => {
|
||
const formParams: FormStepParams = {
|
||
form: this.props.stepForms[step],
|
||
isActiveStep: step === activeStep,
|
||
isCodeSpecified,
|
||
email,
|
||
code,
|
||
};
|
||
|
||
switch (step) {
|
||
case 0:
|
||
return this.renderStep0(formParams);
|
||
case 1:
|
||
return this.renderStep1(formParams);
|
||
case 2:
|
||
return this.renderStep2(formParams);
|
||
}
|
||
})}
|
||
</SlideMotion>
|
||
);
|
||
}
|
||
|
||
renderStep0({ email, form }: FormStepParams): ReactNode {
|
||
return (
|
||
<div key="step0" data-testid="step1" className={styles.formBody}>
|
||
<div className={styles.formRow}>
|
||
<p className={styles.description}>
|
||
<Message key="currentAccountEmail" defaultMessage="Current account E‑mail address:" />
|
||
</p>
|
||
</div>
|
||
|
||
<div className={styles.formRow}>
|
||
<h2 className={changeEmail.currentAccountEmail}>{email}</h2>
|
||
|
||
<FormError error={form.getError('email')} />
|
||
</div>
|
||
|
||
<div className={styles.formRow}>
|
||
<p className={styles.description}>
|
||
<Message
|
||
key="pressButtonToStart"
|
||
defaultMessage="Press the button below to send a message with the code for E‑mail change initialization."
|
||
/>
|
||
</p>
|
||
</div>
|
||
</div>
|
||
);
|
||
}
|
||
|
||
renderStep1({ email, form, code, isCodeSpecified, isActiveStep }: FormStepParams): ReactNode {
|
||
return (
|
||
<div key="step1" data-testid="step2" className={styles.formBody}>
|
||
<div className={styles.formRow}>
|
||
<p className={styles.description}>
|
||
<Message
|
||
key="enterInitializationCode"
|
||
defaultMessage="The E‑mail with an initialization code for E‑mail change procedure was sent to {email}. Please enter the code into the field below:"
|
||
values={{
|
||
email: <b>{email}</b>,
|
||
}}
|
||
/>
|
||
</p>
|
||
</div>
|
||
|
||
<div className={styles.formRow}>
|
||
<Input
|
||
{...form.bindField('key')}
|
||
required={isActiveStep}
|
||
disabled={isCodeSpecified}
|
||
value={code}
|
||
onChange={this.onCodeInput}
|
||
autoComplete="off"
|
||
skin="light"
|
||
color="violet"
|
||
placeholder={labels.codePlaceholder}
|
||
/>
|
||
</div>
|
||
|
||
<div className={styles.formRow}>
|
||
<p className={styles.description}>
|
||
<Message
|
||
key="enterNewEmail"
|
||
defaultMessage="Then provide your new E‑mail address, that you want to use with this account. You will be mailed with confirmation code."
|
||
/>
|
||
</p>
|
||
</div>
|
||
|
||
<div className={styles.formRow}>
|
||
<Input
|
||
{...form.bindField('email')}
|
||
required={isActiveStep}
|
||
skin="light"
|
||
color="violet"
|
||
placeholder={labels.newEmailPlaceholder}
|
||
/>
|
||
</div>
|
||
</div>
|
||
);
|
||
}
|
||
|
||
renderStep2({ form, code, isCodeSpecified, isActiveStep }: FormStepParams): ReactNode {
|
||
const { newEmail } = this.state;
|
||
|
||
return (
|
||
<div key="step2" data-testid="step3" className={styles.formBody}>
|
||
<div className={styles.formRow}>
|
||
<p className={styles.description}>
|
||
{newEmail ? (
|
||
<span>
|
||
<Message
|
||
key="finalizationCodeWasSentToEmail"
|
||
defaultMessage="The E‑mail change confirmation code was sent to {email}."
|
||
values={{
|
||
email: <b>{newEmail}</b>,
|
||
}}
|
||
/>{' '}
|
||
</span>
|
||
) : null}
|
||
<Message
|
||
key="enterFinalizationCode"
|
||
defaultMessage="In order to confirm your new E‑mail, please enter the code received into the field below:"
|
||
/>
|
||
</p>
|
||
</div>
|
||
|
||
<div className={styles.formRow}>
|
||
<Input
|
||
{...form.bindField('key')}
|
||
required={isActiveStep}
|
||
disabled={isCodeSpecified}
|
||
value={code}
|
||
onChange={this.onCodeInput}
|
||
autoComplete="off"
|
||
skin="light"
|
||
color="violet"
|
||
placeholder={labels.codePlaceholder}
|
||
/>
|
||
</div>
|
||
</div>
|
||
);
|
||
}
|
||
|
||
nextStep() {
|
||
const { activeStep } = this.state;
|
||
const nextStep = activeStep + 1;
|
||
let newEmail = null;
|
||
|
||
if (activeStep === 1) {
|
||
newEmail = this.props.stepForms[1].value('email');
|
||
}
|
||
|
||
if (nextStep < STEPS_TOTAL) {
|
||
this.setState({
|
||
activeStep: nextStep as ChangeEmailStep,
|
||
newEmail,
|
||
});
|
||
|
||
this.props.onChangeStep(nextStep as ChangeEmailStep);
|
||
}
|
||
}
|
||
|
||
isLastStep() {
|
||
return this.state.activeStep + 1 === STEPS_TOTAL;
|
||
}
|
||
|
||
onSwitchStep = (event: React.MouseEvent<HTMLAnchorElement>) => {
|
||
event.preventDefault();
|
||
|
||
this.nextStep();
|
||
};
|
||
|
||
onCodeInput = (event: React.ChangeEvent<HTMLInputElement>) => {
|
||
const { value } = event.target;
|
||
|
||
if (this.props.code) {
|
||
return;
|
||
}
|
||
|
||
this.setState({
|
||
code: value,
|
||
});
|
||
};
|
||
|
||
onFormSubmit = () => {
|
||
const { activeStep } = this.state;
|
||
const form = this.props.stepForms[activeStep];
|
||
const promise = this.props.onSubmit(activeStep, form);
|
||
|
||
if (!promise || !promise.then) {
|
||
throw new Error('Expecting promise from onSubmit');
|
||
}
|
||
|
||
promise.then(
|
||
() => {
|
||
this.nextStep();
|
||
this.setState({
|
||
code: '',
|
||
});
|
||
},
|
||
(resp) => {
|
||
if (resp.errors) {
|
||
form.setErrors(resp.errors);
|
||
this.forceUpdate();
|
||
} else {
|
||
return Promise.reject(resp);
|
||
}
|
||
},
|
||
);
|
||
};
|
||
}
|