From dccc68b710485bb167bcf80f27c7fc4024ec3906 Mon Sep 17 00:00:00 2001 From: Keane Chan Date: Sat, 15 Oct 2022 01:35:49 +0800 Subject: [PATCH] [resumes][feat] add edit form functionality (#379) * [resumes][feat] add edit form functionality * [resumes][chore] remove comment --- apps/portal/src/pages/resumes/[resumeId].tsx | 104 ++++++++++++------ apps/portal/src/pages/resumes/submit.tsx | 81 ++++++++++++-- .../resumes/resumes-resume-user-router.ts | 27 ++++- 3 files changed, 164 insertions(+), 48 deletions(-) diff --git a/apps/portal/src/pages/resumes/[resumeId].tsx b/apps/portal/src/pages/resumes/[resumeId].tsx index bdb0a94d..611f0204 100644 --- a/apps/portal/src/pages/resumes/[resumeId].tsx +++ b/apps/portal/src/pages/resumes/[resumeId].tsx @@ -4,12 +4,14 @@ import Error from 'next/error'; import Head from 'next/head'; import { useRouter } from 'next/router'; import { useSession } from 'next-auth/react'; +import { useState } from 'react'; import { AcademicCapIcon, BriefcaseIcon, CalendarIcon, InformationCircleIcon, MapPinIcon, + PencilSquareIcon, StarIcon, } from '@heroicons/react/20/solid'; import { Spinner } from '@tih/ui'; @@ -20,6 +22,8 @@ import ResumeExpandableText from '~/components/resumes/shared/ResumeExpandableTe import { trpc } from '~/utils/trpc'; +import SubmitResumeForm from './submit'; + export default function ResumeReviewPage() { const ErrorPage = ( @@ -46,6 +50,11 @@ export default function ResumeReviewPage() { }, }); + const userIsOwner = + session?.user?.id != null && session.user.id === detailsQuery.data?.userId; + + const [isEditMode, setIsEditMode] = useState(false); + const onStarButtonClick = () => { if (session?.user?.id == null) { router.push('/api/auth/signin'); @@ -65,6 +74,30 @@ export default function ResumeReviewPage() { } }; + const onEditButtonClick = () => { + setIsEditMode(true); + }; + + if (isEditMode && detailsQuery.data != null) { + return ( + { + utils.invalidateQueries(['resumes.resume.findOne']); + setIsEditMode(false); + }} + /> + ); + } + return ( <> {(detailsQuery.isError || detailsQuery.data === null) && ErrorPage} @@ -80,45 +113,46 @@ export default function ResumeReviewPage() { {detailsQuery.data.title}
-
+

{detailsQuery.data.title}

- + {userIsOwner && ( + )} - disabled={ - session?.user === undefined || - starMutation.isLoading || - unstarMutation.isLoading - } - type="button" - onClick={onStarButtonClick}> - -
- {starMutation.isLoading || unstarMutation.isLoading ? ( - - ) : ( -
- Star -
- - {detailsQuery.data?._count.stars} - - +
diff --git a/apps/portal/src/pages/resumes/submit.tsx b/apps/portal/src/pages/resumes/submit.tsx index 49763d8d..12568b25 100644 --- a/apps/portal/src/pages/resumes/submit.tsx +++ b/apps/portal/src/pages/resumes/submit.tsx @@ -60,8 +60,26 @@ const selectors: Array = [ { key: 'location', label: 'Location', options: LOCATION }, ]; -export default function SubmitResumeForm() { - const [resumeFile, setResumeFile] = useState(); +type InitFormDetails = { + additionalInfo?: string; + experience: string; + location: string; + resumeId: string; + role: string; + title: string; + url: string; +}; + +type Props = Readonly<{ + initFormDetails?: InitFormDetails | null; + onClose: () => void; +}>; + +export default function SubmitResumeForm({ + initFormDetails, + onClose = () => undefined, +}: Props) { + const [resumeFile, setResumeFile] = useState(null); const [isLoading, setIsLoading] = useState(false); const [invalidFileUploadError, setInvalidFileUploadError] = useState< string | null @@ -70,7 +88,8 @@ export default function SubmitResumeForm() { const { data: session, status } = useSession(); const router = useRouter(); - const resumeCreateMutation = trpc.useMutation('resumes.resume.user.create'); + const resumeUpsertMutation = trpc.useMutation('resumes.resume.user.upsert'); + const isNewForm = initFormDetails == null; const { register, @@ -81,6 +100,7 @@ export default function SubmitResumeForm() { } = useForm({ defaultValues: { isChecked: false, + ...initFormDetails, }, }); @@ -89,7 +109,9 @@ export default function SubmitResumeForm() { if (fileRejections.length === 0) { setInvalidFileUploadError(''); setResumeFile(acceptedFiles[0]); - setValue('file', acceptedFiles[0]); + setValue('file', acceptedFiles[0], { + shouldDirty: true, + }); } else { setInvalidFileUploadError(FILE_UPLOAD_ERROR); } @@ -106,6 +128,30 @@ export default function SubmitResumeForm() { onDrop: onFileDrop, }); + const fetchFilePdf = useCallback(async () => { + const fileUrl = initFormDetails?.url; + + if (fileUrl == null) { + return; + } + + const data = await axios + .get(fileUrl, { + responseType: 'blob', + }) + .then((res) => res.data); + + const keyAndFileName = fileUrl.substring(fileUrl.indexOf('resumes')); + const fileName = keyAndFileName.substring(keyAndFileName.indexOf('-') + 1); + + const file = new File([data], fileName); + setResumeFile(file); + setValue('file', file, { + shouldDirty: false, + }); + }, [initFormDetails?.url, setValue]); + + // Route user to sign in if not logged in useEffect(() => { if (status !== 'loading') { if (session?.user?.id == null) { @@ -114,6 +160,11 @@ export default function SubmitResumeForm() { } }, [router, session, status]); + // Fetch initial file PDF for edit form + useEffect(() => { + fetchFilePdf(); + }, [fetchFilePdf]); + const onSubmit: SubmitHandler = async (data) => { if (resumeFile == null) { return; @@ -131,10 +182,11 @@ export default function SubmitResumeForm() { }); const { url } = res.data; - resumeCreateMutation.mutate( + resumeUpsertMutation.mutate( { additionalInfo: data.additionalInfo, experience: data.experience, + id: initFormDetails?.resumeId, location: data.location, role: data.role, title: data.title, @@ -148,19 +200,26 @@ export default function SubmitResumeForm() { setIsLoading(false); }, onSuccess() { - router.push('/resumes/browse'); + if (isNewForm) { + router.push('/resumes/browse'); + } else { + onClose(); + } }, }, ); }; const onClickClear = () => { - if (isDirty || resumeFile != null) { + if (isDirty) { setIsDialogShown(true); + } else { + onClose(); } }; const onClickResetDialog = () => { + onClose(); setIsDialogShown(false); reset(); setResumeFile(null); @@ -227,7 +286,11 @@ export default function SubmitResumeForm() { onClick={() => setIsDialogShown(false)} /> } - title="Are you sure you want to clear?" + 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! @@ -346,7 +409,7 @@ export default function SubmitResumeForm() {