[resumes][feat] add required fields and use text area (#329)

* [resumes][feat] add required fields and update UI

* [resumes][refactor] use same lists
This commit is contained in:
Keane Chan
2022-10-08 23:53:22 +08:00
committed by GitHub
parent 101f6c7d70
commit bead5bff14
4 changed files with 66 additions and 81 deletions

View File

@ -23,19 +23,33 @@ export const ROLES = [
label: 'Full-Stack Engineer', label: 'Full-Stack Engineer',
value: 'Full-Stack Engineer', value: 'Full-Stack Engineer',
}, },
{ checked: false, label: 'Frontend Engineer', value: 'frontend-engineer' }, { checked: false, label: 'Frontend Engineer', value: 'Frontend Engineer' },
{ checked: false, label: 'Backend Engineer', value: 'backend-engineer' }, { checked: false, label: 'Backend Engineer', value: 'Backend Engineer' },
{ checked: false, label: 'DevOps Engineer', value: 'devops-engineer' }, { checked: false, label: 'DevOps Engineer', value: 'DevOps Engineer' },
{ checked: false, label: 'iOS Engineer', value: 'ios-engineer' }, { checked: false, label: 'iOS Engineer', value: 'iOS Engineer' },
{ checked: false, label: 'Android Engineer', value: 'android-engineer' }, { checked: false, label: 'Android Engineer', value: 'Android Engineer' },
]; ];
export const EXPERIENCE = [ export const EXPERIENCE = [
{ checked: false, label: 'Freshman', value: 'freshman' }, { checked: false, label: 'Freshman', value: 'Freshman' },
{ checked: false, label: 'Sophomore', value: 'sophomore' }, { checked: false, label: 'Sophomore', value: 'Sophomore' },
{ checked: false, label: 'Junior', value: 'junior' }, { checked: false, label: 'Junior', value: 'Junior' },
{ checked: false, label: 'Senior', value: 'senior' }, { checked: false, label: 'Senior', value: 'Senior' },
{ checked: false, label: 'Fresh Grad (0-1 years)', value: 'freshgrad' }, {
checked: false,
label: 'Fresh Grad (0-1 years)',
value: 'Fresh Grad (0-1 years)',
},
{
checked: false,
label: 'Mid-level (2 - 5 years)',
value: 'Mid-level (2 - 5 years)',
},
{
checked: false,
label: 'Senior (5+ years)',
value: 'Senior (5+ years)',
},
]; ];
export const LOCATION = [ export const LOCATION = [

View File

@ -1,7 +1,7 @@
import { useState } from 'react'; import { useState } from 'react';
import type { SubmitHandler } from 'react-hook-form'; import type { SubmitHandler } from 'react-hook-form';
import { useForm } from 'react-hook-form'; import { useForm } from 'react-hook-form';
import { Button, Dialog, TextInput } from '@tih/ui'; import { Button, Dialog, TextArea } from '@tih/ui';
import { trpc } from '~/utils/trpc'; import { trpc } from '~/utils/trpc';
@ -86,45 +86,39 @@ export default function CommentsForm({
<form <form
className="w-full space-y-8 divide-y divide-gray-200" className="w-full space-y-8 divide-y divide-gray-200"
onSubmit={handleSubmit(onSubmit)}> onSubmit={handleSubmit(onSubmit)}>
{/* TODO: Convert TextInput to TextArea */}
<div className="mt-4 space-y-4"> <div className="mt-4 space-y-4">
<TextInput <TextArea
{...(register('general'), {})} {...(register('general'), {})}
label="General" label="General"
placeholder="General comments about the resume" placeholder="General comments about the resume"
type="text"
onChange={(value) => onValueChange('general', value)} onChange={(value) => onValueChange('general', value)}
/> />
<TextInput <TextArea
{...(register('education'), {})} {...(register('education'), {})}
label="Education" label="Education"
placeholder="Comments about the Education section" placeholder="Comments about the Education section"
type="text"
onChange={(value) => onValueChange('education', value)} onChange={(value) => onValueChange('education', value)}
/> />
<TextInput <TextArea
{...(register('experience'), {})} {...(register('experience'), {})}
label="Experience" label="Experience"
placeholder="Comments about the Experience section" placeholder="Comments about the Experience section"
type="text"
onChange={(value) => onValueChange('experience', value)} onChange={(value) => onValueChange('experience', value)}
/> />
<TextInput <TextArea
{...(register('projects'), {})} {...(register('projects'), {})}
label="Projects" label="Projects"
placeholder="Comments about the Projects section" placeholder="Comments about the Projects section"
type="text"
onChange={(value) => onValueChange('projects', value)} onChange={(value) => onValueChange('projects', value)}
/> />
<TextInput <TextArea
{...(register('skills'), {})} {...(register('skills'), {})}
label="Skills" label="Skills"
placeholder="Comments about the Skills section" placeholder="Comments about the Skills section"
type="text"
onChange={(value) => onValueChange('skills', value)} onChange={(value) => onValueChange('skills', value)}
/> />
</div> </div>

View File

@ -283,7 +283,10 @@ export default function ResumeHomePage() {
<ul role="list"> <ul role="list">
{resumes.map((resumeObj) => ( {resumes.map((resumeObj) => (
<li key={resumeObj.id}> <li key={resumeObj.id}>
<BrowseListItem href="#" resumeInfo={resumeObj} /> <BrowseListItem
href={`resumes/${resumeObj.id}`}
resumeInfo={resumeObj}
/>
</li> </li>
))} ))}
</ul> </ul>

View File

@ -1,10 +1,17 @@
import clsx from 'clsx';
import Head from 'next/head'; import Head from 'next/head';
import { useRouter } from 'next/router'; import { useRouter } from 'next/router';
import { useMemo, useState } from 'react'; import { useMemo, useState } from 'react';
import type { SubmitHandler } from 'react-hook-form'; import type { SubmitHandler } from 'react-hook-form';
import { useForm } from 'react-hook-form'; import { useForm } from 'react-hook-form';
import { PaperClipIcon } from '@heroicons/react/24/outline'; import { PaperClipIcon } from '@heroicons/react/24/outline';
import { Button, Select, TextInput } from '@tih/ui'; import { Button, Select, TextArea, TextInput } from '@tih/ui';
import {
EXPERIENCE,
LOCATION,
ROLES,
} from '~/components/resumes/browse/constants';
import { trpc } from '~/utils/trpc'; import { trpc } from '~/utils/trpc';
@ -13,7 +20,7 @@ const TITLE_PLACEHOLDER =
const ADDITIONAL_INFO_PLACEHOLDER = `e.g. Im applying for company XYZ. I have been resume-rejected by N companies that I have applied for. Please help me to review so company XYZ gives me an interview!`; const ADDITIONAL_INFO_PLACEHOLDER = `e.g. Im applying for company XYZ. I have been resume-rejected by N companies that I have applied for. Please help me to review so company XYZ gives me an interview!`;
const FILE_UPLOAD_ERROR = 'Please upload a PDF file that is less than 10MB.'; const FILE_UPLOAD_ERROR = 'Please upload a PDF file that is less than 10MB.';
const MAX_FILE_SIZE_LIMIT = 10485760; const MAX_FILE_SIZE_LIMIT = 10000000;
type IFormInput = { type IFormInput = {
additionalInfo?: string; additionalInfo?: string;
@ -25,52 +32,6 @@ type IFormInput = {
}; };
export default function SubmitResumeForm() { export default function SubmitResumeForm() {
// TODO: Use enums instead
const roleItems = [
{
label: 'Frontend Engineer',
value: 'Frontend Engineer',
},
{
label: 'Full-Stack Engineer',
value: 'Full-Stack Engineer',
},
{
label: 'Backend Engineer',
value: 'Backend Engineer',
},
];
const experienceItems = [
{
label: 'Fresh Graduate (0 - 1 years)',
value: 'Fresh Graduate (0 - 1 years)',
},
{
label: 'Mid',
value: 'Mid',
},
{
label: 'Senior',
value: 'Senior',
},
];
const locationItems = [
{
label: 'United States',
value: 'United States',
},
{
label: 'Singapore',
value: 'Singapore',
},
{
label: 'India',
value: 'India',
},
];
const resumeCreateMutation = trpc.useMutation('resumes.resume.user.create'); const resumeCreateMutation = trpc.useMutation('resumes.resume.user.create');
const router = useRouter(); const router = useRouter();
@ -139,6 +100,7 @@ export default function SubmitResumeForm() {
errorMessage={errors?.title && 'Title cannot be empty!'} errorMessage={errors?.title && 'Title cannot be empty!'}
label="Title" label="Title"
placeholder={TITLE_PLACEHOLDER} placeholder={TITLE_PLACEHOLDER}
required={true}
onChange={(val) => setValue('title', val)} onChange={(val) => setValue('title', val)}
/> />
</div> </div>
@ -146,7 +108,8 @@ export default function SubmitResumeForm() {
<Select <Select
{...register('role', { required: true })} {...register('role', { required: true })}
label="Role" label="Role"
options={roleItems} options={ROLES}
required={true}
onChange={(val) => setValue('role', val)} onChange={(val) => setValue('role', val)}
/> />
</div> </div>
@ -154,7 +117,8 @@ export default function SubmitResumeForm() {
<Select <Select
{...register('experience', { required: true })} {...register('experience', { required: true })}
label="Experience Level" label="Experience Level"
options={experienceItems} options={EXPERIENCE}
required={true}
onChange={(val) => setValue('experience', val)} onChange={(val) => setValue('experience', val)}
/> />
</div> </div>
@ -163,27 +127,39 @@ export default function SubmitResumeForm() {
{...register('location', { required: true })} {...register('location', { required: true })}
label="Location" label="Location"
name="location" name="location"
options={locationItems} options={LOCATION}
required={true}
onChange={(val) => setValue('location', val)} onChange={(val) => setValue('location', val)}
/> />
</div> </div>
<p className="text-sm font-medium text-slate-700"> <p className="text-sm font-medium text-slate-700">
Upload resume (PDF format) Upload resume (PDF format)
<span aria-hidden="true" className="text-danger-500">
{' '}
*
</span>
</p> </p>
<div className="mb-4"> <div className="mb-4">
<div className="mt-2 flex justify-center rounded-md border-2 border-dashed border-gray-300 px-6 pt-5 pb-6"> <div
className={clsx(
fileUploadError ? 'border-danger-600' : 'border-gray-300',
'mt-2 flex justify-center rounded-md border-2 border-dashed px-6 pt-5 pb-6',
)}>
<div className="space-y-1 text-center"> <div className="space-y-1 text-center">
<div className="flex gap-2"> <div className="flex gap-2">
<PaperClipIcon className="m-auto h-8 w-8 text-gray-600" /> <PaperClipIcon className="m-auto h-8 w-8 text-gray-600" />
{resumeFile && <p>{resumeFile.name}</p>} {resumeFile && <p>{resumeFile.name}</p>}
</div> </div>
<div className="flex text-sm text-gray-600"> <div className="flex justify-center text-sm">
<label <label
className="relative cursor-pointer rounded-md bg-white font-medium text-indigo-600 focus-within:outline-none focus-within:ring-2 focus-within:ring-indigo-500 focus-within:ring-offset-2 hover:text-indigo-500" className="rounded-md focus-within:outline-none focus-within:ring-2 focus-within:ring-indigo-500 focus-within:ring-offset-2"
htmlFor="file-upload"> htmlFor="file-upload">
<span>Upload a file</span> <p className="cursor-pointer font-medium text-indigo-600 hover:text-indigo-500">
Upload a file
</p>
<input <input
{...register('file', { required: true })} {...register('file', { required: true })}
accept="application/pdf"
className="sr-only" className="sr-only"
id="file-upload" id="file-upload"
name="file-upload" name="file-upload"
@ -191,7 +167,6 @@ export default function SubmitResumeForm() {
onChange={onUploadFile} onChange={onUploadFile}
/> />
</label> </label>
<p className="pl-1">or drag and drop</p>
</div> </div>
<p className="text-xs text-gray-500">PDF up to 10MB</p> <p className="text-xs text-gray-500">PDF up to 10MB</p>
</div> </div>
@ -201,8 +176,7 @@ export default function SubmitResumeForm() {
)} )}
</div> </div>
<div className="mb-4"> <div className="mb-4">
{/* TODO: Use TextInputArea instead */} <TextArea
<TextInput
{...register('additionalInfo')} {...register('additionalInfo')}
label="Additional Information" label="Additional Information"
placeholder={ADDITIONAL_INFO_PLACEHOLDER} placeholder={ADDITIONAL_INFO_PLACEHOLDER}