This repository has been archived by the owner on Jul 24, 2023. It is now read-only.
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #23 from NEARFoundation/develop
Adding block of users under 18 and bug fixes
- Loading branch information
Showing
16 changed files
with
9,847 additions
and
2,194 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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> | ||
); | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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> | ||
); | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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> | ||
</> | ||
); | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.