|
|
|
@ -20,7 +20,8 @@ import {
|
|
|
|
|
} from '@tih/ui';
|
|
|
|
|
|
|
|
|
|
import { useGoogleAnalytics } from '~/components/global/GoogleAnalytics';
|
|
|
|
|
import SubmissionGuidelines from '~/components/resumes/submit-form/SubmissionGuidelines';
|
|
|
|
|
import ResumeSubmissionGuidelines from '~/components/resumes/submit-form/ResumeSubmissionGuidelines';
|
|
|
|
|
import Container from '~/components/shared/Container';
|
|
|
|
|
import loginPageHref from '~/components/shared/loginPageHref';
|
|
|
|
|
|
|
|
|
|
import { RESUME_STORAGE_KEY } from '~/constants/file-storage-keys';
|
|
|
|
@ -239,203 +240,206 @@ export default function SubmitResumeForm({
|
|
|
|
|
<Head>
|
|
|
|
|
<title>Upload a Resume</title>
|
|
|
|
|
</Head>
|
|
|
|
|
{status === 'loading' && (
|
|
|
|
|
<div className="w-full pt-4">
|
|
|
|
|
{' '}
|
|
|
|
|
<Spinner display="block" size="lg" />{' '}
|
|
|
|
|
</div>
|
|
|
|
|
)}
|
|
|
|
|
{status === 'authenticated' && (
|
|
|
|
|
<main className="flex-1">
|
|
|
|
|
<section
|
|
|
|
|
aria-labelledby="primary-heading"
|
|
|
|
|
className="flex h-full min-w-0 flex-1 flex-col lg:order-last">
|
|
|
|
|
{/* Reset Dialog component */}
|
|
|
|
|
<Dialog
|
|
|
|
|
isShown={isDialogShown}
|
|
|
|
|
primaryButton={
|
|
|
|
|
<Button
|
|
|
|
|
display="block"
|
|
|
|
|
label="OK"
|
|
|
|
|
variant="primary"
|
|
|
|
|
onClick={onClickResetDialog}
|
|
|
|
|
/>
|
|
|
|
|
}
|
|
|
|
|
secondaryButton={
|
|
|
|
|
<Button
|
|
|
|
|
display="block"
|
|
|
|
|
label="Cancel"
|
|
|
|
|
variant="tertiary"
|
|
|
|
|
onClick={() => setIsDialogShown(false)}
|
|
|
|
|
/>
|
|
|
|
|
}
|
|
|
|
|
title={
|
|
|
|
|
isNewForm
|
|
|
|
|
? 'Are you sure you want to clear?'
|
|
|
|
|
: 'Are you sure you want to leave?'
|
|
|
|
|
}
|
|
|
|
|
onClose={() => setIsDialogShown(false)}>
|
|
|
|
|
Note that your current input will not be saved!
|
|
|
|
|
</Dialog>
|
|
|
|
|
<form
|
|
|
|
|
className="mt-8 w-full max-w-screen-lg space-y-6 self-center rounded-lg bg-white p-10 shadow-lg"
|
|
|
|
|
onSubmit={handleSubmit(onSubmit)}>
|
|
|
|
|
<h1 className="mb-4 text-center text-2xl font-semibold">
|
|
|
|
|
{isNewForm ? 'Upload a resume' : 'Update details'}
|
|
|
|
|
</h1>
|
|
|
|
|
{/* Title Section */}
|
|
|
|
|
<TextInput
|
|
|
|
|
{...(register('title', { required: true }), {})}
|
|
|
|
|
defaultValue={initFormDetails?.title}
|
|
|
|
|
disabled={isLoading}
|
|
|
|
|
errorMessage={
|
|
|
|
|
errors.title?.message != null
|
|
|
|
|
? 'Title cannot be empty'
|
|
|
|
|
: undefined
|
|
|
|
|
<Container variant="xs">
|
|
|
|
|
{status === 'loading' && (
|
|
|
|
|
<div className="w-full pt-4">
|
|
|
|
|
<Spinner display="block" size="lg" />
|
|
|
|
|
</div>
|
|
|
|
|
)}
|
|
|
|
|
{status === 'authenticated' && (
|
|
|
|
|
<main className="flex-1">
|
|
|
|
|
<section
|
|
|
|
|
aria-labelledby="primary-heading"
|
|
|
|
|
className="flex h-full min-w-0 flex-1 flex-col lg:order-last">
|
|
|
|
|
{/* Reset Dialog component */}
|
|
|
|
|
<Dialog
|
|
|
|
|
isShown={isDialogShown}
|
|
|
|
|
primaryButton={
|
|
|
|
|
<Button
|
|
|
|
|
display="block"
|
|
|
|
|
label="OK"
|
|
|
|
|
variant="primary"
|
|
|
|
|
onClick={onClickResetDialog}
|
|
|
|
|
/>
|
|
|
|
|
}
|
|
|
|
|
label="Title"
|
|
|
|
|
placeholder={TITLE_PLACEHOLDER}
|
|
|
|
|
required={true}
|
|
|
|
|
onChange={(val) => onValueChange('title', val)}
|
|
|
|
|
/>
|
|
|
|
|
<div className="flex flex-wrap gap-6">
|
|
|
|
|
<Select
|
|
|
|
|
{...register('role', { required: true })}
|
|
|
|
|
defaultValue={undefined}
|
|
|
|
|
secondaryButton={
|
|
|
|
|
<Button
|
|
|
|
|
display="block"
|
|
|
|
|
label="Cancel"
|
|
|
|
|
variant="tertiary"
|
|
|
|
|
onClick={() => setIsDialogShown(false)}
|
|
|
|
|
/>
|
|
|
|
|
}
|
|
|
|
|
title={
|
|
|
|
|
isNewForm
|
|
|
|
|
? 'Are you sure you want to clear?'
|
|
|
|
|
: 'Are you sure you want to leave?'
|
|
|
|
|
}
|
|
|
|
|
onClose={() => setIsDialogShown(false)}>
|
|
|
|
|
Note that your current input will not be saved!
|
|
|
|
|
</Dialog>
|
|
|
|
|
<form
|
|
|
|
|
className="w-full space-y-6 self-center bg-white p-10 md:my-8 md:rounded-lg md:shadow-lg"
|
|
|
|
|
onSubmit={handleSubmit(onSubmit)}>
|
|
|
|
|
<h1 className="mb-8 text-2xl font-bold text-slate-900 sm:text-center sm:text-4xl">
|
|
|
|
|
{isNewForm ? 'Upload a resume' : 'Update details'}
|
|
|
|
|
</h1>
|
|
|
|
|
{/* Title Section */}
|
|
|
|
|
<TextInput
|
|
|
|
|
{...(register('title', { required: true }), {})}
|
|
|
|
|
defaultValue={initFormDetails?.title}
|
|
|
|
|
disabled={isLoading}
|
|
|
|
|
label="Role"
|
|
|
|
|
options={ROLES}
|
|
|
|
|
placeholder=" "
|
|
|
|
|
errorMessage={
|
|
|
|
|
errors.title?.message != null
|
|
|
|
|
? 'Title cannot be empty'
|
|
|
|
|
: undefined
|
|
|
|
|
}
|
|
|
|
|
label="Title"
|
|
|
|
|
placeholder={TITLE_PLACEHOLDER}
|
|
|
|
|
required={true}
|
|
|
|
|
onChange={(val) => onValueChange('role', val)}
|
|
|
|
|
onChange={(val) => onValueChange('title', val)}
|
|
|
|
|
/>
|
|
|
|
|
<div className="flex flex-wrap gap-6">
|
|
|
|
|
<Select
|
|
|
|
|
{...register('role', { required: true })}
|
|
|
|
|
defaultValue={undefined}
|
|
|
|
|
disabled={isLoading}
|
|
|
|
|
label="Role"
|
|
|
|
|
options={ROLES}
|
|
|
|
|
placeholder=" "
|
|
|
|
|
required={true}
|
|
|
|
|
onChange={(val) => onValueChange('role', val)}
|
|
|
|
|
/>
|
|
|
|
|
<Select
|
|
|
|
|
{...register('experience', { required: true })}
|
|
|
|
|
disabled={isLoading}
|
|
|
|
|
label="Experience Level"
|
|
|
|
|
options={EXPERIENCES}
|
|
|
|
|
placeholder=" "
|
|
|
|
|
required={true}
|
|
|
|
|
onChange={(val) => onValueChange('experience', val)}
|
|
|
|
|
/>
|
|
|
|
|
</div>
|
|
|
|
|
<Select
|
|
|
|
|
{...register('experience', { required: true })}
|
|
|
|
|
{...register('location', { required: true })}
|
|
|
|
|
disabled={isLoading}
|
|
|
|
|
label="Experience Level"
|
|
|
|
|
options={EXPERIENCES}
|
|
|
|
|
label="Location"
|
|
|
|
|
options={LOCATIONS}
|
|
|
|
|
placeholder=" "
|
|
|
|
|
required={true}
|
|
|
|
|
onChange={(val) => onValueChange('experience', val)}
|
|
|
|
|
onChange={(val) => onValueChange('location', val)}
|
|
|
|
|
/>
|
|
|
|
|
</div>
|
|
|
|
|
<Select
|
|
|
|
|
{...register('location', { required: true })}
|
|
|
|
|
disabled={isLoading}
|
|
|
|
|
label="Location"
|
|
|
|
|
options={LOCATIONS}
|
|
|
|
|
placeholder=" "
|
|
|
|
|
required={true}
|
|
|
|
|
onChange={(val) => onValueChange('location', val)}
|
|
|
|
|
/>
|
|
|
|
|
{/* Upload resume form */}
|
|
|
|
|
{isNewForm && (
|
|
|
|
|
<div className="space-y-2">
|
|
|
|
|
<p className="text-sm font-medium text-slate-700">
|
|
|
|
|
Upload resume (PDF format)
|
|
|
|
|
<span aria-hidden="true" className="text-danger-500">
|
|
|
|
|
{' '}
|
|
|
|
|
*
|
|
|
|
|
</span>
|
|
|
|
|
</p>
|
|
|
|
|
<div
|
|
|
|
|
{...getRootProps()}
|
|
|
|
|
className={clsx(
|
|
|
|
|
fileUploadError
|
|
|
|
|
? 'border-danger-600'
|
|
|
|
|
: 'border-slate-300',
|
|
|
|
|
'cursor-pointer flex-col items-center space-y-1 rounded-md border-2 border-dashed bg-slate-100 py-4 px-4 text-center',
|
|
|
|
|
)}>
|
|
|
|
|
<input
|
|
|
|
|
{...register('file', { required: true })}
|
|
|
|
|
{...getInputProps()}
|
|
|
|
|
accept="application/pdf"
|
|
|
|
|
className="sr-only"
|
|
|
|
|
disabled={isLoading}
|
|
|
|
|
id="file-upload"
|
|
|
|
|
name="file-upload"
|
|
|
|
|
type="file"
|
|
|
|
|
/>
|
|
|
|
|
{resumeFile == null ? (
|
|
|
|
|
<ArrowUpCircleIcon className="text-primary-500 m-auto h-10 w-10" />
|
|
|
|
|
) : (
|
|
|
|
|
<p
|
|
|
|
|
className="hover:text-primary-600 cursor-pointer underline underline-offset-1"
|
|
|
|
|
onClick={onClickDownload}>
|
|
|
|
|
{resumeFile.name}
|
|
|
|
|
</p>
|
|
|
|
|
)}
|
|
|
|
|
<label
|
|
|
|
|
className="focus-within:ring-primary-500 cursor-pointer text-sm focus-within:outline-none focus-within:ring-2 focus-within:ring-offset-2"
|
|
|
|
|
htmlFor="file-upload">
|
|
|
|
|
<span className="font-medium">Drop file here</span>
|
|
|
|
|
<span className="mr-1 ml-1 font-light">or</span>
|
|
|
|
|
<span className="text-primary-600 hover:text-primary-400 font-medium">
|
|
|
|
|
{resumeFile == null ? 'Select file' : 'Replace file'}
|
|
|
|
|
{/* Upload resume form */}
|
|
|
|
|
{isNewForm && (
|
|
|
|
|
<div className="space-y-2">
|
|
|
|
|
<p className="text-sm font-medium text-slate-700">
|
|
|
|
|
Upload resume (PDF format)
|
|
|
|
|
<span aria-hidden="true" className="text-danger-500">
|
|
|
|
|
{' '}
|
|
|
|
|
*
|
|
|
|
|
</span>
|
|
|
|
|
</label>
|
|
|
|
|
<p className="text-xs text-slate-500">
|
|
|
|
|
PDF up to {FILE_SIZE_LIMIT_MB}MB
|
|
|
|
|
</p>
|
|
|
|
|
<div
|
|
|
|
|
{...getRootProps()}
|
|
|
|
|
className={clsx(
|
|
|
|
|
fileUploadError
|
|
|
|
|
? 'border-danger-600'
|
|
|
|
|
: 'border-slate-300',
|
|
|
|
|
'cursor-pointer flex-col items-center space-y-1 rounded-md border-2 border-dashed bg-slate-50 py-4 px-4 text-center',
|
|
|
|
|
)}>
|
|
|
|
|
<input
|
|
|
|
|
{...register('file', { required: true })}
|
|
|
|
|
{...getInputProps()}
|
|
|
|
|
accept="application/pdf"
|
|
|
|
|
className="sr-only"
|
|
|
|
|
disabled={isLoading}
|
|
|
|
|
id="file-upload"
|
|
|
|
|
name="file-upload"
|
|
|
|
|
type="file"
|
|
|
|
|
/>
|
|
|
|
|
{resumeFile == null ? (
|
|
|
|
|
<ArrowUpCircleIcon className="text-primary-500 m-auto h-10 w-10" />
|
|
|
|
|
) : (
|
|
|
|
|
<p
|
|
|
|
|
className="hover:text-primary-600 cursor-pointer underline underline-offset-1"
|
|
|
|
|
onClick={onClickDownload}>
|
|
|
|
|
{resumeFile.name}
|
|
|
|
|
</p>
|
|
|
|
|
)}
|
|
|
|
|
<label
|
|
|
|
|
className="focus-within:ring-primary-500 cursor-pointer text-sm focus-within:outline-none focus-within:ring-2 focus-within:ring-offset-2"
|
|
|
|
|
htmlFor="file-upload">
|
|
|
|
|
<span className="font-medium">Drop file here</span>
|
|
|
|
|
<span className="mr-1 ml-1 font-light">or</span>
|
|
|
|
|
<span className="text-primary-600 hover:text-primary-400 font-medium">
|
|
|
|
|
{resumeFile == null ? 'Select file' : 'Replace file'}
|
|
|
|
|
</span>
|
|
|
|
|
</label>
|
|
|
|
|
<p className="text-xs text-slate-500">
|
|
|
|
|
PDF up to {FILE_SIZE_LIMIT_MB}MB
|
|
|
|
|
</p>
|
|
|
|
|
</div>
|
|
|
|
|
{fileUploadError && (
|
|
|
|
|
<p className="text-danger-600 text-sm">
|
|
|
|
|
{fileUploadError}
|
|
|
|
|
</p>
|
|
|
|
|
)}
|
|
|
|
|
</div>
|
|
|
|
|
{fileUploadError && (
|
|
|
|
|
<p className="text-danger-600 text-sm">{fileUploadError}</p>
|
|
|
|
|
)}
|
|
|
|
|
</div>
|
|
|
|
|
)}
|
|
|
|
|
{/* Additional Info Section */}
|
|
|
|
|
<TextArea
|
|
|
|
|
{...(register('additionalInfo'),
|
|
|
|
|
{ defaultValue: initFormDetails?.additionalInfo })}
|
|
|
|
|
disabled={isLoading}
|
|
|
|
|
label="Additional Information"
|
|
|
|
|
placeholder={ADDITIONAL_INFO_PLACEHOLDER}
|
|
|
|
|
onChange={(val) => onValueChange('additionalInfo', val)}
|
|
|
|
|
/>
|
|
|
|
|
{/* Submission Guidelines */}
|
|
|
|
|
{isNewForm && (
|
|
|
|
|
<>
|
|
|
|
|
<SubmissionGuidelines />
|
|
|
|
|
<CheckboxInput
|
|
|
|
|
{...register('isChecked', { required: true })}
|
|
|
|
|
disabled={isLoading}
|
|
|
|
|
errorMessage={
|
|
|
|
|
!errors.file && errors.isChecked
|
|
|
|
|
? 'Please tick the checkbox after reading through the guidelines.'
|
|
|
|
|
: undefined
|
|
|
|
|
}
|
|
|
|
|
label="I have read and will follow the guidelines stated."
|
|
|
|
|
onChange={(val) => {
|
|
|
|
|
if (val) {
|
|
|
|
|
clearErrors('isChecked');
|
|
|
|
|
}
|
|
|
|
|
setValue('isChecked', val);
|
|
|
|
|
}}
|
|
|
|
|
/>
|
|
|
|
|
</>
|
|
|
|
|
)}
|
|
|
|
|
{/* Clear and Submit Buttons */}
|
|
|
|
|
<div className="flex justify-end gap-4">
|
|
|
|
|
<Button
|
|
|
|
|
addonPosition="start"
|
|
|
|
|
disabled={isLoading}
|
|
|
|
|
label={isNewForm ? 'Clear' : 'Cancel'}
|
|
|
|
|
variant="tertiary"
|
|
|
|
|
onClick={onClickClear}
|
|
|
|
|
/>
|
|
|
|
|
<Button
|
|
|
|
|
addonPosition="start"
|
|
|
|
|
)}
|
|
|
|
|
{/* Additional Info Section */}
|
|
|
|
|
<TextArea
|
|
|
|
|
{...(register('additionalInfo'),
|
|
|
|
|
{ defaultValue: initFormDetails?.additionalInfo })}
|
|
|
|
|
disabled={isLoading}
|
|
|
|
|
isLoading={isLoading}
|
|
|
|
|
label="Submit"
|
|
|
|
|
type="submit"
|
|
|
|
|
variant="primary"
|
|
|
|
|
label="Additional Information"
|
|
|
|
|
placeholder={ADDITIONAL_INFO_PLACEHOLDER}
|
|
|
|
|
onChange={(val) => onValueChange('additionalInfo', val)}
|
|
|
|
|
/>
|
|
|
|
|
</div>
|
|
|
|
|
</form>
|
|
|
|
|
</section>
|
|
|
|
|
</main>
|
|
|
|
|
)}
|
|
|
|
|
{/* Submission Guidelines */}
|
|
|
|
|
{isNewForm && (
|
|
|
|
|
<div className="space-y-4">
|
|
|
|
|
<ResumeSubmissionGuidelines />
|
|
|
|
|
<CheckboxInput
|
|
|
|
|
{...register('isChecked', { required: true })}
|
|
|
|
|
disabled={isLoading}
|
|
|
|
|
errorMessage={
|
|
|
|
|
!errors.file && errors.isChecked
|
|
|
|
|
? 'Please tick the checkbox after reading through the guidelines.'
|
|
|
|
|
: undefined
|
|
|
|
|
}
|
|
|
|
|
label="I have read and followed the above guidelines."
|
|
|
|
|
onChange={(val) => {
|
|
|
|
|
if (val) {
|
|
|
|
|
clearErrors('isChecked');
|
|
|
|
|
}
|
|
|
|
|
setValue('isChecked', val);
|
|
|
|
|
}}
|
|
|
|
|
/>
|
|
|
|
|
</div>
|
|
|
|
|
)}
|
|
|
|
|
{/* Clear and Submit Buttons */}
|
|
|
|
|
<div className="flex justify-end gap-4">
|
|
|
|
|
<Button
|
|
|
|
|
addonPosition="start"
|
|
|
|
|
disabled={isLoading}
|
|
|
|
|
label={isNewForm ? 'Clear' : 'Cancel'}
|
|
|
|
|
variant="tertiary"
|
|
|
|
|
onClick={onClickClear}
|
|
|
|
|
/>
|
|
|
|
|
<Button
|
|
|
|
|
addonPosition="start"
|
|
|
|
|
disabled={isLoading}
|
|
|
|
|
isLoading={isLoading}
|
|
|
|
|
label="Submit"
|
|
|
|
|
type="submit"
|
|
|
|
|
variant="primary"
|
|
|
|
|
/>
|
|
|
|
|
</div>
|
|
|
|
|
</form>
|
|
|
|
|
</section>
|
|
|
|
|
</main>
|
|
|
|
|
)}
|
|
|
|
|
</Container>
|
|
|
|
|
</>
|
|
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|