mirror of
https://github.com/yangshun/tech-interview-handbook.git
synced 2025-07-28 04:33:42 +08:00
[resumes][feat] remove resume title, clean up submit form (#415)
* [resumes][refactor] clean up submit form * [resumes][feat] remove resume title * [resumes][feat] remove resume title
This commit is contained in:
@ -1,13 +0,0 @@
|
|||||||
import { Badge } from '@tih/ui';
|
|
||||||
|
|
||||||
export default function ResumeReviewsTitle() {
|
|
||||||
return (
|
|
||||||
<div>
|
|
||||||
<h1 className="mb-1 text-2xl font-bold">Resume Reviews</h1>
|
|
||||||
<Badge
|
|
||||||
label="Check out reviewed resumes or look for resumes to review"
|
|
||||||
variant="info"
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
}
|
|
@ -65,7 +65,7 @@ export default function ResumeListItem({ href, resumeInfo }: Props) {
|
|||||||
</div>
|
</div>
|
||||||
<div className="mt-2 text-slate-400">{resumeInfo.location}</div>
|
<div className="mt-2 text-slate-400">{resumeInfo.location}</div>
|
||||||
</div>
|
</div>
|
||||||
<ChevronRightIcon className="col-span-1 w-8 self-center justify-self-center" />
|
<ChevronRightIcon className="col-span-1 w-8 self-center justify-self-center text-slate-400" />
|
||||||
</div>
|
</div>
|
||||||
</Link>
|
</Link>
|
||||||
);
|
);
|
||||||
|
@ -1,22 +1,21 @@
|
|||||||
export default function SubmissionGuidelines() {
|
export default function SubmissionGuidelines() {
|
||||||
return (
|
return (
|
||||||
<div className="mb-4 text-left text-sm text-slate-700">
|
<div className="text-left text-sm text-slate-700">
|
||||||
<h2 className="mb-2 text-xl font-medium">Submission Guidelines</h2>
|
<h2 className="mb-2 text-xl font-medium">Submission Guidelines</h2>
|
||||||
<p>
|
<p>
|
||||||
Before you submit, please review and acknolwedge our
|
Before you submit, please review and acknowledge our
|
||||||
<span className="font-bold"> submission guidelines </span>
|
<span className="font-bold"> submission guidelines </span>
|
||||||
stated below.
|
stated below.
|
||||||
</p>
|
</p>
|
||||||
<p>
|
<p>
|
||||||
<span className="text-lg font-bold">• </span>
|
<span className="text-lg font-bold">• </span>
|
||||||
Ensure that you do not divulge any of your
|
Ensure that you do not divulge any of your{' '}
|
||||||
<span className="font-bold"> personal particulars</span>.
|
<span className="font-bold">personal particulars</span>.
|
||||||
</p>
|
</p>
|
||||||
<p>
|
<p>
|
||||||
<span className="text-lg font-bold">• </span>
|
<span className="text-lg font-bold">• </span>
|
||||||
Ensure that you do not divulge any
|
Ensure that you do not divulge any{' '}
|
||||||
<span className="font-bold">
|
<span className="font-bold">
|
||||||
{' '}
|
|
||||||
company's proprietary and confidential information
|
company's proprietary and confidential information
|
||||||
</span>
|
</span>
|
||||||
.
|
.
|
||||||
|
@ -4,10 +4,10 @@ import { useSession } from 'next-auth/react';
|
|||||||
import { Fragment, useEffect, useState } from 'react';
|
import { Fragment, useEffect, useState } from 'react';
|
||||||
import { Dialog, Disclosure, Transition } from '@headlessui/react';
|
import { Dialog, Disclosure, Transition } from '@headlessui/react';
|
||||||
import { FunnelIcon, MinusIcon, PlusIcon } from '@heroicons/react/20/solid';
|
import { FunnelIcon, MinusIcon, PlusIcon } from '@heroicons/react/20/solid';
|
||||||
import { XMarkIcon } from '@heroicons/react/24/outline';
|
|
||||||
import {
|
import {
|
||||||
MagnifyingGlassIcon,
|
MagnifyingGlassIcon,
|
||||||
NewspaperIcon,
|
NewspaperIcon,
|
||||||
|
XMarkIcon,
|
||||||
} from '@heroicons/react/24/outline';
|
} from '@heroicons/react/24/outline';
|
||||||
import {
|
import {
|
||||||
CheckboxInput,
|
CheckboxInput,
|
||||||
@ -36,7 +36,6 @@ import {
|
|||||||
SORT_OPTIONS,
|
SORT_OPTIONS,
|
||||||
} from '~/components/resumes/browse/resumeFilters';
|
} from '~/components/resumes/browse/resumeFilters';
|
||||||
import ResumeListItems from '~/components/resumes/browse/ResumeListItems';
|
import ResumeListItems from '~/components/resumes/browse/ResumeListItems';
|
||||||
import ResumeReviewsTitle from '~/components/resumes/ResumeReviewsTitle';
|
|
||||||
import ResumeSignInButton from '~/components/resumes/shared/ResumeSignInButton';
|
import ResumeSignInButton from '~/components/resumes/shared/ResumeSignInButton';
|
||||||
|
|
||||||
import useDebounceValue from '~/utils/resumes/useDebounceValue';
|
import useDebounceValue from '~/utils/resumes/useDebounceValue';
|
||||||
@ -369,12 +368,8 @@ export default function ResumeHomePage() {
|
|||||||
</Transition.Root>
|
</Transition.Root>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<main className="h-[calc(100vh-4rem)] flex-1 overflow-y-scroll">
|
<main className="h-[calc(100vh-4rem)] flex-auto overflow-y-scroll px-8 pt-6 pb-4">
|
||||||
<div className="ml-8 py-4">
|
<div className="flex justify-start">
|
||||||
<ResumeReviewsTitle />
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div className="mx-8 mt-4 flex justify-start">
|
|
||||||
<div className="hidden w-1/6 pt-2 lg:block">
|
<div className="hidden w-1/6 pt-2 lg:block">
|
||||||
<h3 className="text-md font-medium tracking-tight text-gray-900">
|
<h3 className="text-md font-medium tracking-tight text-gray-900">
|
||||||
Shortcuts
|
Shortcuts
|
||||||
@ -535,44 +530,41 @@ export default function ResumeHomePage() {
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div className="mb-6">
|
{isFetchingResumes ? (
|
||||||
{isFetchingResumes ? (
|
<div className="w-full pt-4">
|
||||||
<div className="w-full pt-4">
|
{' '}
|
||||||
{' '}
|
<Spinner display="block" size="lg" />{' '}
|
||||||
<Spinner display="block" size="lg" />{' '}
|
</div>
|
||||||
</div>
|
) : sessionData === null && tabsValue !== BROWSE_TABS_VALUES.ALL ? (
|
||||||
) : sessionData === null &&
|
<ResumeSignInButton
|
||||||
tabsValue !== BROWSE_TABS_VALUES.ALL ? (
|
className="mt-8"
|
||||||
<ResumeSignInButton
|
text={getLoggedOutText(tabsValue)}
|
||||||
className="mt-8"
|
/>
|
||||||
text={getLoggedOutText(tabsValue)}
|
) : getTabResumes().length === 0 ? (
|
||||||
|
<div className="mt-24 flex flex-wrap justify-center">
|
||||||
|
<NewspaperIcon
|
||||||
|
className="mb-12 basis-full"
|
||||||
|
height={196}
|
||||||
|
width={196}
|
||||||
/>
|
/>
|
||||||
) : getTabResumes().length === 0 ? (
|
{getEmptyDataText(tabsValue, searchValue, userFilters)}
|
||||||
<div className="mt-24 flex flex-wrap justify-center">
|
</div>
|
||||||
<NewspaperIcon
|
) : (
|
||||||
className="mb-12 basis-full"
|
<>
|
||||||
height={196}
|
<ResumeListItems resumes={getTabResumes()} />
|
||||||
width={196}
|
{getTabTotalPages() > 1 && (
|
||||||
/>
|
<div className="mt-4 flex justify-center">
|
||||||
{getEmptyDataText(tabsValue, searchValue, userFilters)}
|
<Pagination
|
||||||
</div>
|
current={currentPage}
|
||||||
) : (
|
end={getTabTotalPages()}
|
||||||
<>
|
label="pagination"
|
||||||
<ResumeListItems resumes={getTabResumes()} />
|
start={1}
|
||||||
{getTabTotalPages() > 1 && (
|
onSelect={(page) => setCurrentPage(page)}
|
||||||
<div className="my-4 flex justify-center">
|
/>
|
||||||
<Pagination
|
</div>
|
||||||
current={currentPage}
|
)}
|
||||||
end={getTabTotalPages()}
|
</>
|
||||||
label="pagination"
|
)}
|
||||||
start={1}
|
|
||||||
onSelect={(page) => setCurrentPage(page)}
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
)}
|
|
||||||
</>
|
|
||||||
)}
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</main>
|
</main>
|
||||||
|
@ -46,6 +46,8 @@ type IFormInput = {
|
|||||||
title: string;
|
title: string;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
type InputKeys = keyof IFormInput;
|
||||||
|
|
||||||
type InitFormDetails = {
|
type InitFormDetails = {
|
||||||
additionalInfo?: string;
|
additionalInfo?: string;
|
||||||
experience: string;
|
experience: string;
|
||||||
@ -218,6 +220,10 @@ export default function SubmitResumeForm({
|
|||||||
}
|
}
|
||||||
}, [errors?.file, invalidFileUploadError]);
|
}, [errors?.file, invalidFileUploadError]);
|
||||||
|
|
||||||
|
const onValueChange = (section: InputKeys, value: string) => {
|
||||||
|
setValue(section, value.trim(), { shouldTouch: false });
|
||||||
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<Head>
|
<Head>
|
||||||
@ -269,7 +275,7 @@ export default function SubmitResumeForm({
|
|||||||
required={true}
|
required={true}
|
||||||
onChange={(val) => setValue('title', val)}
|
onChange={(val) => setValue('title', val)}
|
||||||
/>
|
/>
|
||||||
<div className="flex gap-12">
|
<div className="flex gap-8">
|
||||||
<Select
|
<Select
|
||||||
{...register('role', { required: true })}
|
{...register('role', { required: true })}
|
||||||
defaultValue={undefined}
|
defaultValue={undefined}
|
||||||
@ -301,7 +307,7 @@ export default function SubmitResumeForm({
|
|||||||
/>
|
/>
|
||||||
{/* Upload resume form */}
|
{/* Upload resume form */}
|
||||||
{isNewForm && (
|
{isNewForm && (
|
||||||
<>
|
<div className="space-y-2">
|
||||||
<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 aria-hidden="true" className="text-danger-500">
|
||||||
@ -309,66 +315,60 @@ export default function SubmitResumeForm({
|
|||||||
*
|
*
|
||||||
</span>
|
</span>
|
||||||
</p>
|
</p>
|
||||||
<div className="mb-4">
|
<div
|
||||||
<div
|
{...getRootProps()}
|
||||||
{...getRootProps()}
|
className={clsx(
|
||||||
className={clsx(
|
fileUploadError ? 'border-danger-600' : 'border-gray-300',
|
||||||
fileUploadError ? 'border-danger-600' : 'border-gray-300',
|
'flex cursor-pointer justify-center rounded-md border-2 border-dashed bg-gray-100 py-4',
|
||||||
'mt-2 flex cursor-pointer justify-center rounded-md border-2 border-dashed bg-gray-100 px-6 pt-5 pb-6',
|
)}>
|
||||||
)}>
|
<div className="space-y-1 text-center">
|
||||||
<div className="space-y-1 text-center">
|
{resumeFile == null ? (
|
||||||
{resumeFile == null ? (
|
<ArrowUpCircleIcon className="m-auto h-10 w-10 text-indigo-500" />
|
||||||
<ArrowUpCircleIcon className="m-auto h-10 w-10 text-indigo-500" />
|
) : (
|
||||||
) : (
|
<p
|
||||||
<p
|
className="cursor-pointer underline underline-offset-1 hover:text-indigo-600"
|
||||||
className="cursor-pointer underline underline-offset-1 hover:text-indigo-600"
|
onClick={onClickDownload}>
|
||||||
onClick={onClickDownload}>
|
{resumeFile.name}
|
||||||
{resumeFile.name}
|
|
||||||
</p>
|
|
||||||
)}
|
|
||||||
<div className="flex items-center text-sm">
|
|
||||||
<label
|
|
||||||
className="rounded-md focus-within:outline-none focus-within:ring-2 focus-within:ring-indigo-500 focus-within:ring-offset-2"
|
|
||||||
htmlFor="file-upload">
|
|
||||||
<span className="mt-2 font-medium">
|
|
||||||
Drop file here
|
|
||||||
</span>
|
|
||||||
<span className="mr-1 ml-1 font-light">or</span>
|
|
||||||
<span className="cursor-pointer font-medium text-indigo-600 hover:text-indigo-400">
|
|
||||||
{resumeFile == null
|
|
||||||
? 'Select file'
|
|
||||||
: 'Replace file'}
|
|
||||||
</span>
|
|
||||||
<input
|
|
||||||
{...register('file', { required: true })}
|
|
||||||
{...getInputProps()}
|
|
||||||
accept="application/pdf"
|
|
||||||
className="sr-only"
|
|
||||||
disabled={isLoading}
|
|
||||||
id="file-upload"
|
|
||||||
name="file-upload"
|
|
||||||
type="file"
|
|
||||||
/>
|
|
||||||
</label>
|
|
||||||
</div>
|
|
||||||
<p className="text-xs text-gray-500">
|
|
||||||
PDF up to {FILE_SIZE_LIMIT_MB}MB
|
|
||||||
</p>
|
</p>
|
||||||
|
)}
|
||||||
|
<div className="flex items-center text-sm">
|
||||||
|
<label
|
||||||
|
className="rounded-md focus-within:outline-none focus-within:ring-2 focus-within:ring-indigo-500 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="cursor-pointer font-medium text-indigo-600 hover:text-indigo-400">
|
||||||
|
{resumeFile == null ? 'Select file' : 'Replace file'}
|
||||||
|
</span>
|
||||||
|
<input
|
||||||
|
{...register('file', { required: true })}
|
||||||
|
{...getInputProps()}
|
||||||
|
accept="application/pdf"
|
||||||
|
className="sr-only"
|
||||||
|
disabled={isLoading}
|
||||||
|
id="file-upload"
|
||||||
|
name="file-upload"
|
||||||
|
type="file"
|
||||||
|
/>
|
||||||
|
</label>
|
||||||
</div>
|
</div>
|
||||||
|
<p className="text-xs text-gray-500">
|
||||||
|
PDF up to {FILE_SIZE_LIMIT_MB}MB
|
||||||
|
</p>
|
||||||
</div>
|
</div>
|
||||||
{fileUploadError && (
|
|
||||||
<p className="text-danger-600 text-sm">{fileUploadError}</p>
|
|
||||||
)}
|
|
||||||
</div>
|
</div>
|
||||||
</>
|
{fileUploadError && (
|
||||||
|
<p className="text-danger-600 text-sm">{fileUploadError}</p>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
)}
|
)}
|
||||||
{/* Additional Info Section */}
|
{/* Additional Info Section */}
|
||||||
<TextArea
|
<TextArea
|
||||||
{...register('additionalInfo')}
|
{...(register('additionalInfo'), {})}
|
||||||
disabled={isLoading}
|
disabled={isLoading}
|
||||||
label="Additional Information"
|
label="Additional Information"
|
||||||
placeholder={ADDITIONAL_INFO_PLACEHOLDER}
|
placeholder={ADDITIONAL_INFO_PLACEHOLDER}
|
||||||
onChange={(val) => setValue('additionalInfo', val)}
|
onChange={(val) => onValueChange('additionalInfo', val)}
|
||||||
/>
|
/>
|
||||||
{/* Submission Guidelines */}
|
{/* Submission Guidelines */}
|
||||||
{isNewForm && (
|
{isNewForm && (
|
||||||
|
Reference in New Issue
Block a user