Form refactoring (#142)

This commit is contained in:
Riccardo Graziosi
2022-07-22 16:50:36 +02:00
committed by GitHub
parent d078e659c6
commit 9592ac3d1d
22 changed files with 557 additions and 1060 deletions

View File

@@ -1,92 +1,83 @@
import * as React from 'react';
import { SubmitHandler, useForm } from 'react-hook-form';
import I18n from 'i18n-js';
import Box from '../common/Box';
import { TenantSignUpTenantFormState } from '../../reducers/tenantSignUpReducer';
import Button from '../common/Button';
import Spinner from '../common/Spinner';
import { DangerText } from '../common/CustomTexts';
import { ITenantSignUpTenantForm } from './TenantSignUpP';
import HttpStatus from '../../constants/http_status';
interface Props {
tenantForm: TenantSignUpTenantFormState;
handleChangeTenantSiteName(siteName: string): void;
handleChangeTenantSubdomain(subdomain: string): void;
isSubmitting: boolean;
error: string;
handleSubmit(): void;
handleSignUpSubmit(siteName: string, subdomain: string): void;
}
class TenantSignUpForm extends React.Component<Props> {
form: any;
constructor(props: Props) {
super(props);
this.form = React.createRef();
const TenantSignUpForm = ({
isSubmitting,
error,
handleSignUpSubmit,
}: Props) => {
const { register, handleSubmit, formState: { errors } } = useForm<ITenantSignUpTenantForm>();
const onSubmit: SubmitHandler<ITenantSignUpTenantForm> = data => {
handleSignUpSubmit(data.siteName, data.subdomain);
}
render() {
const {
tenantForm,
handleChangeTenantSiteName,
handleChangeTenantSubdomain,
return (
<Box customClass="tenantSignUpStep2">
<h3>{ I18n.t('signup.step2.title') }</h3>
isSubmitting,
error,
handleSubmit,
} = this.props;
<form onSubmit={handleSubmit(onSubmit)}>
<div className="formRow">
<input
{...register('siteName', { required: true })}
autoFocus
placeholder={I18n.t('signup.step2.site_name')}
id="tenantSiteName"
className="formControl"
/>
<DangerText>{errors.siteName && I18n.t('signup.step2.validations.site_name')}</DangerText>
</div>
return (
<Box customClass="tenantSignUpStep2">
<h3>{ I18n.t('signup.step2.title') }</h3>
<form ref={this.form}>
<div className="formRow">
<div className="formRow">
<div className="input-group">
<input
type="text"
autoFocus
value={tenantForm.siteName}
onChange={e => handleChangeTenantSiteName(e.target.value)}
placeholder={I18n.t('signup.step2.site_name')}
required
id="tenantSiteName"
{...register('subdomain', {
required: true,
validate: async (newSubdomain) => {
const res = await fetch(`/is_available?new_subdomain=${newSubdomain}`);
return res.status === HttpStatus.OK;
},
})}
placeholder={I18n.t('signup.step2.subdomain')}
id="tenantSubdomain"
className="formControl"
/>
</div>
<div className="formRow">
<div className="input-group">
<input
type="text"
value={tenantForm.subdomain}
onChange={e => handleChangeTenantSubdomain(e.target.value)}
placeholder={I18n.t('signup.step2.subdomain')}
required
id="tenantSubdomain"
className="formControl"
/>
<div className="input-group-append">
<div className="input-group-text">.astuto.io</div>
</div>
<div className="input-group-append">
<div className="input-group-text">.astuto.io</div>
</div>
</div>
<DangerText>
{errors.subdomain?.type === 'required' && I18n.t('signup.step2.validations.subdomain')}
</DangerText>
<DangerText>
{errors.subdomain?.type === 'validate' && I18n.t('signup.step2.validations.subdomain_already_taken')}
</DangerText>
</div>
<Button
onClick={e => {
e.preventDefault();
handleSubmit();
}}
className="tenantConfirm"
>
{ isSubmitting ? <Spinner /> : I18n.t('signup.step2.create_button') }
</Button>
<Button
onClick={() => null}
className="tenantConfirm"
>
{ isSubmitting ? <Spinner /> : I18n.t('signup.step2.create_button') }
</Button>
{ error !== '' && <DangerText>{ error }</DangerText> }
</form>
</Box>
);
}
{ error !== '' && <DangerText>{ error }</DangerText> }
</form>
</Box>
);
}
export default TenantSignUpForm;

View File

@@ -1,31 +1,15 @@
import * as React from 'react';
import { TenantSignUpTenantFormState, TenantSignUpUserFormState } from '../../reducers/tenantSignUpReducer';
import { useState } from 'react';
import HttpStatus from '../../constants/http_status';
import ConfirmSignUpPage from './ConfirmSignUpPage';
import TenantSignUpForm from './TenantSignUpForm';
import UserSignUpForm from './UserSignUpForm';
interface Props {
authenticityToken: string;
currentStep: number;
emailAuth: boolean;
isSubmitting: boolean;
error: string;
toggleEmailAuth(): void;
userForm: TenantSignUpUserFormState;
handleChangeUserFullName(fullName: string): void;
handleChangeUserEmail(email: string): void;
handleChangeUserPassword(password: string): void;
handleChangeUserPasswordConfirmation(passwordConfirmation: string): void;
handleUserFormConfirm(): void;
tenantForm: TenantSignUpTenantFormState;
handleChangeTenantSiteName(siteName: string): void;
handleChangeTenantSubdomain(subdomain: string): void;
handleSubmit(
userFullName: string,
userEmail: string,
@@ -33,92 +17,92 @@ interface Props {
siteName: string,
subdomain: string,
authenticityToken: string,
): void;
): Promise<any>;
authenticityToken: string;
}
class TenantSignUpP extends React.Component<Props> {
constructor(props: Props) {
super(props);
export interface ITenantSignUpUserForm {
fullName: string;
email: string;
password: string;
passwordConfirmation: string;
}
this._handleSubmit = this._handleSubmit.bind(this);
}
export interface ITenantSignUpTenantForm {
siteName: string;
subdomain: string;
}
_handleSubmit() {
const { userForm, tenantForm, handleSubmit } = this.props;
const TenantSignUpP = ({
isSubmitting,
error,
handleSubmit,
authenticityToken
}: Props) => {
const [userData, setUserData] = useState({
fullName: '',
email: '',
password: '',
passwordConfirmation: '',
});
const [tenantData, setTenantData] = useState({
siteName: '',
subdomain: '',
});
const [currentStep, setCurrentStep] = useState(1);
const [emailAuth, setEmailAuth] = useState(false);
const handleSignUpSubmit = (siteName: string, subdomain: string) => {
handleSubmit(
userForm.fullName,
userForm.email,
userForm.password,
tenantForm.siteName,
tenantForm.subdomain,
this.props.authenticityToken,
);
userData.fullName,
userData.email,
userData.password,
siteName,
subdomain,
authenticityToken,
).then(res => {
if (res?.status !== HttpStatus.Created) return;
setTenantData({ siteName, subdomain });
setCurrentStep(currentStep + 1);
});
}
render() {
const {
currentStep,
emailAuth,
toggleEmailAuth,
return (
<div className="tenantSignUpContainer">
{
(currentStep === 1 || currentStep === 2) &&
<UserSignUpForm
currentStep={currentStep}
setCurrentStep={setCurrentStep}
emailAuth={emailAuth}
setEmailAuth={setEmailAuth}
userData={userData}
setUserData={setUserData}
/>
}
userForm,
handleChangeUserFullName,
handleChangeUserEmail,
handleChangeUserPassword,
handleChangeUserPasswordConfirmation,
handleUserFormConfirm,
{
currentStep === 2 &&
<TenantSignUpForm
isSubmitting={isSubmitting}
error={error}
handleSignUpSubmit={handleSignUpSubmit}
/>
}
tenantForm,
handleChangeTenantSiteName,
handleChangeTenantSubdomain,
isSubmitting,
error,
} = this.props;
return (
<div className="tenantSignUpContainer">
{
(currentStep === 1 || currentStep === 2) &&
<UserSignUpForm
currentStep={currentStep}
emailAuth={emailAuth}
toggleEmailAuth={toggleEmailAuth}
userForm={userForm}
handleChangeUserFullName={handleChangeUserFullName}
handleChangeUserEmail={handleChangeUserEmail}
handleChangeUserPassword={handleChangeUserPassword}
handleChangeUserPasswordConfirmation={handleChangeUserPasswordConfirmation}
handleUserFormConfirm={handleUserFormConfirm}
/>
}
{
currentStep === 2 &&
<TenantSignUpForm
tenantForm={tenantForm}
handleChangeTenantSiteName={handleChangeTenantSiteName}
handleChangeTenantSubdomain={handleChangeTenantSubdomain}
isSubmitting={isSubmitting}
error={error}
handleSubmit={this._handleSubmit}
/>
}
{
currentStep === 3 &&
<ConfirmSignUpPage
subdomain={tenantForm.subdomain}
userEmail={userForm.email}
/>
}
</div>
);
}
{
currentStep === 3 &&
<ConfirmSignUpPage
subdomain={tenantData.subdomain}
userEmail={userData.email}
/>
}
</div>
);
}
export default TenantSignUpP;

View File

@@ -1,147 +1,115 @@
import * as React from 'react';
import { SubmitHandler, useForm } from 'react-hook-form';
import I18n from 'i18n-js';
import Box from '../common/Box';
import Button from '../common/Button';
import { TenantSignUpUserFormState } from '../../reducers/tenantSignUpReducer';
import { ITenantSignUpUserForm } from './TenantSignUpP';
import { DangerText } from '../common/CustomTexts';
interface Props {
currentStep: number;
setCurrentStep(step: number): void;
emailAuth: boolean;
toggleEmailAuth(): void;
userForm: TenantSignUpUserFormState;
handleChangeUserFullName(fullName: string): void;
handleChangeUserEmail(email: string): void;
handleChangeUserPassword(password: string): void;
handleChangeUserPasswordConfirmation(passwordConfirmation: string): void;
handleUserFormConfirm(): void;
setEmailAuth(enabled: boolean): void;
userData: ITenantSignUpUserForm;
setUserData({}: ITenantSignUpUserForm): void;
}
class UserSignUpForm extends React.Component<Props> {
form: any;
const UserSignUpForm = ({
currentStep,
setCurrentStep,
emailAuth,
setEmailAuth,
userData,
setUserData,
}: Props) => {
const { register, handleSubmit, setError, formState: { errors } } = useForm<ITenantSignUpUserForm>();
const onSubmit: SubmitHandler<ITenantSignUpUserForm> = data => {
if (data.password !== data.passwordConfirmation) {
setError('passwordConfirmation', I18n.t('signup.step1.validations.password_mismatch'));
return;
}
constructor(props: Props) {
super(props);
this.form = React.createRef();
setUserData({...data});
setCurrentStep(currentStep + 1);
}
validateUserForm(): boolean {
let isValid: boolean = this.form.current.reportValidity();
if (this.validateUserPasswordConfirmation() === false)
isValid = false;
return (
<Box customClass="tenantSignUpStep1">
<h3>{ I18n.t('signup.step1.title') }</h3>
return isValid;
}
{
currentStep === 1 && !emailAuth &&
<Button className="emailAuth" onClick={() => setEmailAuth(true)}>
{ I18n.t('signup.step1.email_auth') }
</Button>
}
validateUserPasswordConfirmation(): boolean {
const isValid = this.props.userForm.password === this.props.userForm.passwordConfirmation;
return isValid;
}
{
currentStep === 1 && emailAuth &&
<form onSubmit={handleSubmit(onSubmit)}>
<div className="formRow">
<input
{...register('fullName', { required: true, minLength: 2 })}
autoFocus
placeholder={I18n.t('common.forms.auth.full_name')}
id="userFullName"
className="formControl"
/>
<DangerText>{ errors.fullName && I18n.t('signup.step1.validations.full_name') }</DangerText>
</div>
render() {
const {
currentStep,
emailAuth,
toggleEmailAuth,
userForm,
handleChangeUserFullName,
handleChangeUserEmail,
handleChangeUserPassword,
handleChangeUserPasswordConfirmation,
handleUserFormConfirm,
} = this.props;
<div className="formRow">
<input
{...register('email', { required: true, pattern: /(.+)@(.+){2,}\.(.+){2,}/ })}
type="email"
placeholder={I18n.t('common.forms.auth.email')}
id="userEmail"
className="formControl"
/>
<DangerText>{ errors.email && I18n.t('signup.step1.validations.email') }</DangerText>
</div>
return (
<Box customClass="tenantSignUpStep1">
<h3>{ I18n.t('signup.step1.title') }</h3>
<div className="formRow">
<div className="formGroup col-6">
<input
{...register('password', { required: true, minLength: 6, maxLength: 128 })}
type="password"
placeholder={I18n.t('common.forms.auth.password')}
id="userPassword"
className="formControl"
/>
<DangerText>{ errors.password && I18n.t('signup.step1.validations.password', { n: 6 }) }</DangerText>
</div>
{
currentStep === 1 && !emailAuth &&
<Button className="emailAuth" onClick={toggleEmailAuth}>
{ I18n.t('signup.step1.email_auth') }
<div className="formGroup col-6">
<input
{...register('passwordConfirmation')}
type="password"
placeholder={I18n.t('common.forms.auth.password_confirmation')}
id="userPasswordConfirmation"
className="formControl"
/>
<DangerText>{ errors.passwordConfirmation && I18n.t('signup.step1.validations.password_mismatch') }</DangerText>
</div>
</div>
<Button
onClick={() => null}
className="userConfirm"
>
{ I18n.t('common.buttons.confirm') }
</Button>
}
</form>
}
{
currentStep === 1 && emailAuth &&
<form ref={this.form}>
<div className="formRow">
<input
type="text"
autoFocus
value={userForm.fullName}
onChange={e => handleChangeUserFullName(e.target.value)}
placeholder={I18n.t('common.forms.auth.full_name')}
required
id="userFullName"
className="formControl"
/>
</div>
<div className="formRow">
<input
type="email"
value={userForm.email}
onChange={e => handleChangeUserEmail(e.target.value)}
placeholder={I18n.t('common.forms.auth.email')}
required
id="userEmail"
className="formControl"
/>
</div>
<div className="formRow">
<div className="formGroup col-6">
<input
type="password"
value={userForm.password}
onChange={e => handleChangeUserPassword(e.target.value)}
placeholder={I18n.t('common.forms.auth.password')}
required
minLength={6}
maxLength={128}
id="userPassword"
className="formControl"
/>
</div>
<div className="formGroup col-6">
<input
type="password"
value={userForm.passwordConfirmation}
onChange={e => handleChangeUserPasswordConfirmation(e.target.value)}
placeholder={I18n.t('common.forms.auth.password_confirmation')}
required
minLength={6}
maxLength={128}
id="userPasswordConfirmation"
className={`formControl${userForm.passwordConfirmationError ? ' invalid' : ''}`}
/>
</div>
</div>
<Button
onClick={e => {
e.preventDefault();
this.validateUserForm() && handleUserFormConfirm();
}}
className="userConfirm"
>
{ I18n.t('common.buttons.confirm') }
</Button>
</form>
}
{
currentStep === 2 &&
<p><b>{userForm.fullName}</b> ({userForm.email})</p>
}
</Box>
);
}
{
currentStep === 2 &&
<p><b>{userData.fullName}</b> ({userData.email})</p>
}
</Box>
);
}
export default UserSignUpForm;