Skip to content
This repository has been archived by the owner on Jul 24, 2023. It is now read-only.

Commit

Permalink
Merge pull request #23 from NEARFoundation/develop
Browse files Browse the repository at this point in the history
Adding block of users under 18 and bug fixes
  • Loading branch information
sandoche authored Nov 11, 2022
2 parents b1bcac1 + 353a007 commit a08f86d
Show file tree
Hide file tree
Showing 16 changed files with 9,847 additions and 2,194 deletions.
1 change: 1 addition & 0 deletions .eslintrc.js
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@ module.exports = {
'simple-import-sort/exports': 'error',
'@typescript-eslint/prefer-nullish-coalescing': 'error',
'no-magic-numbers': 'error',
'react/require-default-props': 'off',
},
reportUnusedDisableDirectives: true, // https://eslint.org/docs/user-guide/configuring#report-unused-eslint-disable-comments
settings: {
Expand Down
10 changes: 10 additions & 0 deletions components/common/Alert.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
export default function Alert({ children }: { children: React.ReactNode }): JSX.Element {
return (
<div className="alert alert-warning d-flex text-start" role="alert">
<div className="me-3 mt-1">
<i className="fa fa-exclamation-triangle" aria-hidden="true" />
</div>
<div>{children}</div>
</div>
);
}
17 changes: 17 additions & 0 deletions components/common/ErrorRuntime.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
import MainLayout from '../layout/MainLayout';
import ResultsRetryButton from '../results/ResultsRetryButton';

import CenteredCard from './CenteredCard';
import CenteredCardContent from './CenteredCardContent';

export default function ErrorRuntime(): JSX.Element {
return (
<MainLayout>
<CenteredCard>
<CenteredCardContent title="An error occurred" description="Sorry, an error occurred. Please try again." iconClasses="fa fa-exclamation-circle text-warning mb-4">
<ResultsRetryButton autoRetry={false} />
</CenteredCardContent>
</CenteredCard>
</MainLayout>
);
}
176 changes: 135 additions & 41 deletions components/form/ApplicantForm.tsx
Original file line number Diff line number Diff line change
@@ -1,61 +1,155 @@
import { SubmitHandler, useForm, Validate } from 'react-hook-form';

import { FORBIDDEN_CHARACTERS, MIN_AGE_FOR_APPLICANT } from '../../constants';
import type ApplicantProperties from '../../types/ApplicantProperties';
import Alert from '../common/Alert';
import PrivacyPolicyButtonModal from '../privacy-policy/PrivacyPolicyButtonModal';
// https://getbootstrap.com/docs/5.0/forms/floating-labels/
// https://getbootstrap.com/docs/5.0/forms/layout/
// https://getbootstrap.com/docs/5.0/layout/gutters/

export default function ApplicantForm({ onSubmit, loading }: { onSubmit: (event: React.SyntheticEvent) => void; loading: boolean }): JSX.Element {
// eslint-disable-next-line max-lines-per-function
export default function ApplicantForm({ onSubmit, loading, error }: { onSubmit: SubmitHandler<ApplicantProperties>; loading: boolean; error: boolean }): JSX.Element {
const {
register,
handleSubmit,
formState: { errors },
} = useForm<ApplicantProperties>({ mode: 'onTouched' });

// eslint-disable-next-line no-magic-numbers
const YEAR_IN_MILLISECONDS = 1000 * 60 * 60 * 24 * 365.2425;
const EMAIL_VALIDATION_REGEX =
/^(([^<>()[\]\\.,;:\s@"]+(\.[^<>()[\]\\.,;:\s@"]+)*)|(".+"))@((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\])|(([a-zA-Z\-0-9]+\.)+[a-zA-Z]{2,}))$/;

const validateName: Validate<string> = (name: string) => {
const forbiddenCharactersRegex = /[\^!#$%*=<>;{}"]/; // Check FORBIDDEN_CHARACTERS in constants/index.tsx
return !forbiddenCharactersRegex.test(name);
};

const validateMinAge: Validate<string> = (dateOfBirth: string) => {
const age = Math.floor((Date.now() - new Date(dateOfBirth).getTime()) / YEAR_IN_MILLISECONDS);
return age >= MIN_AGE_FOR_APPLICANT;
};

const trimInput = (input: string) => input.trim();

return (
<form className="applicant-form mt-5" onSubmit={onSubmit}>
<div className="row">
<div className="col-md-6 pb-2">
<div className="form-floating">
<input name="firstName" type="text" className="form-control" aria-label="First Name" disabled={loading} required />
{/* eslint-disable-next-line jsx-a11y/label-has-associated-control */}
<label htmlFor="firstName">First Name</label>
<>
{error && <Alert>Sorry, an error occurred. Please review your information and try again.</Alert>}
<form className="applicant-form mt-5" onSubmit={handleSubmit(onSubmit)}>
<div className="row">
<div className="col-md-6 pb-2">
<div className="form-floating">
<input
type="text"
className="form-control"
aria-label="First Name"
disabled={loading}
{...register('firstName', { required: true, setValueAs: trimInput, validate: validateName })}
required
/>
{/* eslint-disable-next-line jsx-a11y/label-has-associated-control */}
<label htmlFor="firstName">First Name</label>
{errors.firstName && (
<p role="alert" className="d-block invalid-feedback text-start mb-0">
{errors.firstName.type === 'required' && 'First name is required'}
{errors.firstName.type === 'validate' && `First name cannot contain special characters such as ${FORBIDDEN_CHARACTERS}`}
</p>
)}
</div>
</div>
<div className="col-md-6 pb-2">
<div className="form-floating">
<input
type="text"
className="form-control"
aria-label="Last Name"
disabled={loading}
{...register('lastName', { required: true, setValueAs: trimInput, validate: validateName })}
required
/>
{/* eslint-disable-next-line jsx-a11y/label-has-associated-control */}
<label htmlFor="lastName">Last Name</label>
{errors.lastName && (
<p role="alert" className="d-block invalid-feedback text-start mb-0">
{errors.lastName.type === 'required' && 'Last name is required'}
{errors.lastName.type === 'validate' && `Last name cannot contain special characters such as ${FORBIDDEN_CHARACTERS}`}
</p>
)}
</div>
</div>
</div>
<div className="col-md-6 pb-2">

<div className="pb-2">
<div className="form-floating">
<input name="lastName" type="text" className="form-control" aria-label="Last Name" disabled={loading} required />
<input
type="email"
className="form-control"
aria-label="email"
disabled={loading}
{...register('email', {
required: true,
pattern: EMAIL_VALIDATION_REGEX,
setValueAs: trimInput,
})}
required
/>
{/* eslint-disable-next-line jsx-a11y/label-has-associated-control */}
<label htmlFor="lastName">Last Name</label>
<label htmlFor="email">Email Address</label>
{errors.email && (
<p role="alert" className="d-block invalid-feedback text-start mb-0">
A valid email address is required
</p>
)}
</div>
</div>
</div>

<div className="pb-2">
<div className="form-floating">
<input name="email" type="email" className="form-control" aria-label="email" disabled={loading} required />
{/* eslint-disable-next-line jsx-a11y/label-has-associated-control */}
<label htmlFor="email">Email Address</label>
<div className="pb-2">
<div className="form-floating">
<input
type="date"
className="form-control"
aria-label="Date of birth"
disabled={loading}
{...register('dob', {
required: true,
validate: validateMinAge,
})}
required
/>
{/* eslint-disable-next-line jsx-a11y/label-has-associated-control */}
<label htmlFor="dob">Date of Birth</label>
{errors.dob && (
<p role="alert" className="d-block invalid-feedback text-start mb-0">
Sorry, we can only verify people who are at least 18 years old
</p>
)}
</div>
</div>
</div>

<div className="pb-2">
<div className="form-floating">
<input name="dob" type="date" className="form-control" aria-label="Date of birth" disabled={loading} required />
{/* eslint-disable-next-line jsx-a11y/label-has-associated-control */}
<label htmlFor="dob">Date of Birth</label>
<div className="pb-2 mt-2">
<div className="form-floating text-start">
<div className="form-check">
<input id="consent" type="checkbox" className="form-check-input" disabled={loading} {...register('consent', { required: true })} required />
{/* eslint-disable-next-line jsx-a11y/label-has-associated-control */}
<label htmlFor="consent" className="form-check-label">
I have read and agree to the <PrivacyPolicyButtonModal>privacy policy</PrivacyPolicyButtonModal>
</label>
</div>
{errors.consent && (
<p role="alert" className="d-block invalid-feedback text-start mb-0">
You must agree to the privacy policy before submitting
</p>
)}
</div>
</div>
</div>

<div className="pb-2 mt-2">
<div className="form-floating text-start">
<div className="form-check">
<input id="consent" name="consent" type="checkbox" className="form-check-input" disabled={loading} required />
{/* eslint-disable-next-line jsx-a11y/label-has-associated-control */}
<label htmlFor="consent" className="form-check-label">
I have read and agree to the <PrivacyPolicyButtonModal>privacy policy</PrivacyPolicyButtonModal>
</label>
</div>
<div className="d-grid mt-2">
<button name="submit" type="submit" className="btn btn-lg btn-primary" disabled={loading}>
Start {loading ? <span className="spinner-border spinner-border-sm" role="status" aria-hidden="true" /> : <i className="fa fa-chevron-right" aria-hidden="true" />}
</button>
</div>
</div>

<div className="d-grid mt-2">
<button name="submit" type="submit" className="btn btn-lg btn-primary" disabled={loading}>
Start {loading ? <span className="spinner-border spinner-border-sm" role="status" aria-hidden="true" /> : <i className="fa fa-chevron-right" aria-hidden="true" />}
</button>
</div>
</form>
</form>
</>
);
}
17 changes: 15 additions & 2 deletions components/form/FirstStep.tsx
Original file line number Diff line number Diff line change
@@ -1,11 +1,23 @@
import { SubmitHandler } from 'react-hook-form';
import type { SdkHandle } from 'onfido-sdk-ui';

import type ApplicantProperties from '../../types/ApplicantProperties';
import CenteredCard from '../common/CenteredCard';
import Header from '../layout/Header';

import ApplicantForm from './ApplicantForm';

function FirstStep({ onfidoInstance, onSubmit, loading }: { onfidoInstance: SdkHandle | null; onSubmit: (event: React.SyntheticEvent) => void; loading: boolean }): JSX.Element {
function FirstStep({
onfidoInstance,
onSubmit,
loading,
error,
}: {
onfidoInstance: SdkHandle | null;
onSubmit: SubmitHandler<ApplicantProperties>;
loading: boolean;
error: boolean;
}): JSX.Element {
return onfidoInstance ? (
<div />
) : (
Expand All @@ -14,8 +26,9 @@ function FirstStep({ onfidoInstance, onSubmit, loading }: { onfidoInstance: SdkH
<h3 className="mb-4">We want to get to know you!</h3>
<p>Start by introducing yourself here.</p>
<p>On the next page, we&apos;ll ask you to provide other information (documents or photos) that will help verify your identity.</p>
<p>Note: applicants younger than 18 years old will not be verified.</p>

<ApplicantForm onSubmit={onSubmit} loading={loading} />
<ApplicantForm onSubmit={onSubmit} loading={loading} error={error} />
</CenteredCard>
);
}
Expand Down
2 changes: 1 addition & 1 deletion components/results/ResultsError.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import ResultsRetryButton from './ResultsRetryButton';

export default function ResultsFailure(): JSX.Element {
return (
<CenteredCardContent title="An error occured" description="Sorry, an error occurred; we invite you to try again." iconClasses="fa fa-exclamation-circle text-warning mb-4">
<CenteredCardContent title="An error occurred" description="Sorry, an error occurred; we invite you to try again." iconClasses="fa fa-exclamation-circle text-warning mb-4">
<ResultsRetryButton />
</CenteredCardContent>
);
Expand Down
6 changes: 4 additions & 2 deletions components/results/ResultsRetryButton.tsx
Original file line number Diff line number Diff line change
@@ -1,8 +1,10 @@
import Link from 'next/link';

export default function ResultsRetryButton(): JSX.Element {
export default function ResultsRetryButton({ autoRetry = true }: { autoRetry?: boolean }): JSX.Element {
const link = autoRetry ? `/${process.env.NEXT_PUBLIC_KYC_ENDPOINT_KEY}?retry=1` : `/${process.env.NEXT_PUBLIC_KYC_ENDPOINT_KEY}`;

return (
<Link href={`/${process.env.NEXT_PUBLIC_KYC_ENDPOINT_KEY}?retry=1`} passHref>
<Link href={link} passHref>
{/* eslint-disable-next-line jsx-a11y/anchor-is-valid */}
<a className="btn btn-primary">
Try again <i className="fa fa-repeat" aria-hidden="true" />
Expand Down
2 changes: 2 additions & 0 deletions constants/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,3 +3,5 @@ export const COOKIE_CHECK_ID_NAME = 'onfido-check-id';
export const LOCALSTORAGE_USER_DATA_NAME = 'onfido-user-data';
export const SHORT_POLLING_INTERVAL = 1000; // 1 second - Short polling applies when the check is in progress
export const LONG_POLLING_INTERVAL = 30_000; // 30 seconds - Long polling applies when the check is under manual approval
export const MIN_AGE_FOR_APPLICANT = 18;
export const FORBIDDEN_CHARACTERS = '^!#$%*=<>;{}"'; // https://documentation.onfido.com/#forbidden-characters
Loading

0 comments on commit a08f86d

Please sign in to comment.