Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions src/components/AuthorizerRoot.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import { useAuthorizer } from '../contexts/AuthorizerContext';
import { StyledWrapper } from '../styledComponents';
import { Views } from '../constants';
import { AuthorizerSignup } from './AuthorizerSignup';
import type { FormFieldsOverrides } from './AuthorizerSignup';
import { AuthorizerForgotPassword } from './AuthorizerForgotPassword';
import { AuthorizerSocialLogin } from './AuthorizerSocialLogin';
import { AuthorizerMagicLinkLogin } from './AuthorizerMagicLinkLogin';
Expand All @@ -19,13 +20,15 @@ export const AuthorizerRoot: FC<{
onForgotPassword?: (data: any) => void;
onPasswordReset?: () => void;
roles?: string[];
signupFieldsOverrides?: FormFieldsOverrides
}> = ({
onLogin,
onSignup,
onMagicLinkLogin,
onForgotPassword,
onPasswordReset,
roles,
signupFieldsOverrides
}) => {
const [view, setView] = useState(Views.Login);
const { config } = useAuthorizer();
Expand Down Expand Up @@ -79,6 +82,7 @@ export const AuthorizerRoot: FC<{
onSignup={onSignup}
urlProps={urlProps}
roles={roles}
fieldOverrides={signupFieldsOverrides}
/>
)}

Expand Down
252 changes: 104 additions & 148 deletions src/components/AuthorizerSignup.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -14,13 +14,27 @@ import { OtpDataType } from '../types';
import { AuthorizerVerifyOtp } from './AuthorizerVerifyOtp';
import { getEmailPhoneLabels, getEmailPhonePlaceholder } from '../utils/labels';

interface InputDataType {
given_name: string | null;
family_name: string | null;
email_or_phone_number: string | null;
password: string | null;
confirmPassword: string | null;
}
type Field =
| 'given_name'
| 'family_name'
| 'email_or_phone_number'
| 'password'
| 'confirmPassword';

type FieldOverride = {
label: string;
placeholder: string;
hide?: boolean;
notRequired?: boolean;
};

type InputDataType = {
[K in Field]: string | null;
};

export type FormFieldsOverrides = {
[K in Field]?: FieldOverride;
};

const initOtpData: OtpDataType = {
is_screen_visible: false,
Expand All @@ -33,7 +47,8 @@ export const AuthorizerSignup: FC<{
onSignup?: (data: AuthToken) => void;
urlProps?: Record<string, any>;
roles?: string[];
}> = ({ setView, onSignup, urlProps, roles }) => {
fieldOverrides?: FormFieldsOverrides;
}> = ({ setView, onSignup, urlProps, roles, fieldOverrides }) => {
const [error, setError] = useState(``);
const [loading, setLoading] = useState(false);
const [otpData, setOtpData] = useState<OtpDataType>({ ...initOtpData });
Expand All @@ -55,9 +70,8 @@ export const AuthorizerSignup: FC<{
const { authorizerRef, config, setAuthData } = useAuthorizer();
const [disableSignupButton, setDisableSignupButton] = useState(false);

const onInputChange = async (field: string, value: string) => {
const onInputChange = async (field: string, value: string) =>
setFormData({ ...formData, [field]: value });
};

const onSubmit = async (e: any) => {
e.preventDefault();
Expand All @@ -81,8 +95,8 @@ export const AuthorizerSignup: FC<{
return;
}
const data: SignupInput = {
email: email,
phone_number: phone_number,
email,
phone_number,
given_name: formData.given_name || '',
family_name: formData.family_name || '',
password: formData.password || '',
Expand Down Expand Up @@ -151,11 +165,15 @@ export const AuthorizerSignup: FC<{
}
};

const onErrorClose = () => {
setError(``);
};
const onErrorClose = () => setError(``);

useEffect(() => {
if (
fieldOverrides?.given_name?.notRequired ||
fieldOverrides?.given_name?.hide
) {
return;
}
if ((formData.given_name || '').trim() === '') {
setErrorData({ ...errorData, given_name: 'First Name is required' });
} else {
Expand All @@ -164,6 +182,12 @@ export const AuthorizerSignup: FC<{
}, [formData.given_name]);

useEffect(() => {
if (
fieldOverrides?.family_name?.notRequired ||
fieldOverrides?.family_name?.hide
) {
return;
}
if ((formData.family_name || '').trim() === '') {
setErrorData({ ...errorData, family_name: 'Last Name is required' });
} else {
Expand Down Expand Up @@ -247,6 +271,55 @@ export const AuthorizerSignup: FC<{
);
}

const renderField = (
key: Field,
label: string,
placeholder: string,
type?: 'text' | 'password'
) => {
const fieldOverride = fieldOverrides?.[key];
if (fieldOverride?.hide) {
return null;
}
return (
<div className={styles['styled-form-group']}>
<label
className={styles['form-input-label']}
htmlFor={`authorizer-sign-up-${key}`}
>
{!fieldOverride?.notRequired && <span>* </span>}
{fieldOverride?.label ?? label}
</label>
<input
name={key}
id={`authorizer-sign-up-${key}`}
className={`${styles['form-input-field']} ${
errorData[key] ? styles['input-error-content'] : null
}`}
placeholder={fieldOverride?.placeholder ?? placeholder}
type={type}
value={formData[key] || ''}
onChange={e => onInputChange(key, e.target.value)}
/>
{errorData[key] && (
<div className={styles['form-input-error']}>{errorData[key]}</div>
)}
</div>
);
};

const shouldFieldBlockSubmit = (key: Field) => {
if (
(formData[key] ||
fieldOverrides?.[key]?.notRequired ||
fieldOverrides?.[key]?.hide) &&
!errorData[key]
) {
return false;
}
return true;
};

return (
<>
{error && (
Expand All @@ -260,135 +333,20 @@ export const AuthorizerSignup: FC<{
!config.is_magic_link_login_enabled && (
<>
<form onSubmit={onSubmit} name="authorizer-sign-up-form">
<div className={styles['styled-form-group']}>
<label
className={styles['form-input-label']}
htmlFor="authorizer-sign-up-given-name"
>
<span>* </span>First Name
</label>
<input
name="given_name"
id="authorizer-sign-up-given-name"
className={`${styles['form-input-field']} ${
errorData.given_name ? styles['input-error-content'] : null
}`}
placeholder="eg. John"
type="text"
value={formData.given_name || ''}
onChange={e => onInputChange('given_name', e.target.value)}
/>
{errorData.given_name && (
<div className={styles['form-input-error']}>
{errorData.given_name}
</div>
)}
</div>
<div className={styles['styled-form-group']}>
<label
className={styles['form-input-label']}
htmlFor="authorizer-sign-up-family-name"
>
<span>* </span>Last Name
</label>
<input
name="family_name"
id="authorizer-sign-up-family-name"
className={`${styles['form-input-field']} ${
errorData.family_name ? styles['input-error-content'] : null
}`}
placeholder="eg. Doe"
type="text"
value={formData.family_name || ''}
onChange={e => onInputChange('family_name', e.target.value)}
/>
{errorData.family_name && (
<div className={styles['form-input-error']}>
{errorData.family_name}
</div>
)}
</div>
<div className={styles['styled-form-group']}>
<label
className={styles['form-input-label']}
htmlFor="authorizer-sign-up-email-or-phone-number"
>
<span>* </span>
{getEmailPhoneLabels(config)}
</label>
<input
name="email_or_phone_number"
id="authorizer-sign-up-email-or-phone-number"
className={`${styles['form-input-field']} ${
errorData.email_or_phone_number
? styles['input-error-content']
: null
}`}
placeholder={getEmailPhonePlaceholder(config)}
type="text"
value={formData.email_or_phone_number || ''}
onChange={e =>
onInputChange('email_or_phone_number', e.target.value)
}
/>
{errorData.email_or_phone_number && (
<div className={styles['form-input-error']}>
{errorData.email_or_phone_number}
</div>
)}
</div>
<div className={styles['styled-form-group']}>
<label
className={styles['form-input-label']}
htmlFor="authorizer-sign-up-password"
>
<span>* </span>Password
</label>
<input
name="password"
id="authorizer-sign-up-password"
className={`${styles['form-input-field']} ${
errorData.password ? styles['input-error-content'] : null
}`}
placeholder="********"
type="password"
value={formData.password || ''}
onChange={e => onInputChange('password', e.target.value)}
/>
{errorData.password && (
<div className={styles['form-input-error']}>
{errorData.password}
</div>
)}
</div>
<div className={styles['styled-form-group']}>
<label
className={styles['form-input-label']}
htmlFor="authorizer-sign-up-confirm-password"
>
<span>* </span>Confirm Password
</label>
<input
name="confirmPassword"
id="authorizer-sign-up-confirm-password"
className={`${styles['form-input-field']} ${
errorData.confirmPassword
? styles['input-error-content']
: null
}`}
placeholder="********"
type="password"
value={formData.confirmPassword || ''}
onChange={e =>
onInputChange('confirmPassword', e.target.value)
}
/>
{errorData.confirmPassword && (
<div className={styles['form-input-error']}>
{errorData.confirmPassword}
</div>
)}
</div>
{renderField('given_name', 'First Name', 'eg. John', 'text')}
{renderField('family_name', 'Last Name', 'eg. Doe', 'text')}
{renderField(
'email_or_phone_number',
getEmailPhoneLabels(config),
getEmailPhonePlaceholder(config)
)}
{renderField('password', 'Password', '********', 'password')}
{renderField(
'confirmPassword',
'Confirm Password',
'********',
'password'
)}
{config.is_strong_password_enabled && (
<>
<PasswordStrengthIndicator
Expand All @@ -404,13 +362,11 @@ export const AuthorizerSignup: FC<{
disabled={
loading ||
disableSignupButton ||
!!errorData.given_name ||
!!errorData.family_name ||
shouldFieldBlockSubmit('given_name') ||
shouldFieldBlockSubmit('family_name') ||
!!errorData.email_or_phone_number ||
!!errorData.password ||
!!errorData.confirmPassword ||
!formData.given_name ||
!formData.family_name ||
!formData.email_or_phone_number ||
!formData.password ||
!formData.confirmPassword
Expand Down