From 70006d411591bebce1e812f9a2558098ba07bb3f Mon Sep 17 00:00:00 2001 From: Ai Ling <50992674+ailing35@users.noreply.github.com> Date: Thu, 3 Nov 2022 21:08:02 +0800 Subject: [PATCH] [offers][fix] Refactor UI (#500) --- .../offers/offerAnalysis/OfferAnalysis.tsx | 8 +- .../offers/offerAnalysis/OfferProfileCard.tsx | 14 +- .../offersSubmission/OffersProfileSave.tsx | 18 +- .../OffersSubmissionAnalysis.tsx | 1 + .../offersSubmission/OffersSubmissionForm.tsx | 27 ++- .../submissionForm/BackgroundForm.tsx | 45 +++-- .../components/offers/profile/OfferCard.tsx | 45 ++--- .../pages/offers/profile/[offerProfileId].tsx | 102 ++++++----- .../offers/submit/result/[offerProfileId].tsx | 160 +++++++++--------- apps/portal/src/utils/offers/time.tsx | 15 ++ 10 files changed, 232 insertions(+), 203 deletions(-) diff --git a/apps/portal/src/components/offers/offerAnalysis/OfferAnalysis.tsx b/apps/portal/src/components/offers/offerAnalysis/OfferAnalysis.tsx index e7e6199b..d229eb52 100644 --- a/apps/portal/src/components/offers/offerAnalysis/OfferAnalysis.tsx +++ b/apps/portal/src/components/offers/offerAnalysis/OfferAnalysis.tsx @@ -109,13 +109,13 @@ export default function OfferAnalysis({ return (
- {isError && ( + {isError ? (

An error occurred while generating profile analysis.

- )} - {isLoading && } - {!isError && !isLoading && ( + ) : isLoading ? ( + + ) : (
- + ); } diff --git a/apps/portal/src/components/offers/offersSubmission/OffersProfileSave.tsx b/apps/portal/src/components/offers/offersSubmission/OffersProfileSave.tsx index d494912e..513d0de7 100644 --- a/apps/portal/src/components/offers/offersSubmission/OffersProfileSave.tsx +++ b/apps/portal/src/components/offers/offersSubmission/OffersProfileSave.tsx @@ -1,5 +1,5 @@ import { signIn, useSession } from 'next-auth/react'; -import { useState } from 'react'; +import type { UseQueryResult } from 'react-query'; import { DocumentDuplicateIcon } from '@heroicons/react/20/solid'; import { BookmarkIcon as BookmarkOutlineIcon } from '@heroicons/react/24/outline'; import { BookmarkIcon as BookmarkSolidIcon } from '@heroicons/react/24/solid'; @@ -11,6 +11,7 @@ import { copyProfileLink, getProfileLink } from '~/utils/offers/link'; import { trpc } from '~/utils/trpc'; type OfferProfileSaveProps = Readonly<{ + isSavedQuery: UseQueryResult; profileId: string; token?: string; }>; @@ -18,10 +19,10 @@ type OfferProfileSaveProps = Readonly<{ export default function OffersProfileSave({ profileId, token, + isSavedQuery: { data: isSaved, isLoading }, }: OfferProfileSaveProps) { const { showToast } = useToast(); const { event: gaEvent } = useGoogleAnalytics(); - const [isSaved, setSaved] = useState(false); const { data: session, status } = useSession(); const saveMutation = trpc.useMutation( @@ -47,15 +48,6 @@ export default function OffersProfileSave({ }, ); - const isSavedQuery = trpc.useQuery( - [`offers.profile.isSaved`, { profileId, userId: session?.user?.id }], - { - onSuccess: (res) => { - setSaved(res); - }, - }, - ); - const trpcContext = trpc.useContext(); const handleSave = () => { if (status === 'unauthenticated') { @@ -125,9 +117,9 @@ export default function OffersProfileSave({

diff --git a/apps/portal/src/components/offers/offersSubmission/OffersSubmissionForm.tsx b/apps/portal/src/components/offers/offersSubmission/OffersSubmissionForm.tsx index 42eff26c..a03782dd 100644 --- a/apps/portal/src/components/offers/offersSubmission/OffersSubmissionForm.tsx +++ b/apps/portal/src/components/offers/offersSubmission/OffersSubmissionForm.tsx @@ -4,7 +4,7 @@ import type { SubmitHandler } from 'react-hook-form'; import { FormProvider, useForm } from 'react-hook-form'; import { ArrowLeftIcon, ArrowRightIcon } from '@heroicons/react/20/solid'; import { JobType } from '@prisma/client'; -import { Button, useToast } from '@tih/ui'; +import { Button, Spinner, useToast } from '@tih/ui'; import { useGoogleAnalytics } from '~/components/global/GoogleAnalytics'; import type { BreadcrumbStep } from '~/components/offers/Breadcrumbs'; @@ -116,7 +116,7 @@ export default function OffersSubmissionForm({ const { handleSubmit, trigger, - formState: { isSubmitting, isSubmitSuccessful }, + formState: { isSubmitting }, } = formMethods; const generateAnalysisMutation = trpc.useMutation( @@ -124,6 +124,10 @@ export default function OffersSubmissionForm({ { onError(error) { console.error(error.message); + showToast({ + title: 'Error generating analysis.', + variant: 'failure', + }); }, onSuccess() { router.push( @@ -174,7 +178,7 @@ export default function OffersSubmissionForm({ title: editProfileId && editToken ? 'Error updating offer profile.' - : 'Error creating offer profile', + : 'Error creating offer profile.', variant: 'failure', }); }, @@ -193,7 +197,7 @@ export default function OffersSubmissionForm({ const onSubmit: SubmitHandler = async (data) => { const result = await trigger(); - if (!result || isSubmitting || isSubmitSuccessful) { + if (!result || isSubmitting || createOrUpdateMutation.isLoading) { return; } @@ -272,7 +276,9 @@ export default function OffersSubmissionForm({ // eslint-disable-next-line react-hooks/exhaustive-deps }, []); - return ( + return generateAnalysisMutation.isLoading ? ( + + ) : (
@@ -324,9 +330,16 @@ export default function OffersSubmissionForm({ }} />
)} - {duration && ( + {!!duration && (
-

{`${duration} months`}

+

{getDurationDisplayText(duration)}

)}
@@ -99,24 +101,27 @@ export default function OfferCard({ return (
- {totalCompensation && ( -
-
- Total Compensation -
-
- {totalCompensation} -
-
- )} - {monthlySalary && ( -
-
- Monthly Salary -
-
{monthlySalary}
-
- )} + {jobType === JobType.FULLTIME + ? totalCompensation && ( +
+
+ Total Compensation +
+
+ {totalCompensation} +
+
+ ) + : monthlySalary && ( +
+
+ Monthly Salary +
+
+ {monthlySalary} +
+
+ )} {base && (
diff --git a/apps/portal/src/pages/offers/profile/[offerProfileId].tsx b/apps/portal/src/pages/offers/profile/[offerProfileId].tsx index c410dccf..f15e38f0 100644 --- a/apps/portal/src/pages/offers/profile/[offerProfileId].tsx +++ b/apps/portal/src/pages/offers/profile/[offerProfileId].tsx @@ -79,6 +79,7 @@ export default function OfferProfile() { jobTitle: getLabelForJobTitleType( res.offersFullTime.title as JobTitleType, ), + jobType: res.jobType, location: res.location, negotiationStrategy: res.negotiationStrategy, otherComment: res.comments, @@ -99,6 +100,7 @@ export default function OfferProfile() { jobTitle: getLabelForJobTitleType( res.offersIntern!.title as JobTitleType, ), + jobType: res.jobType, location: res.location, monthlySalary: convertMoneyToString( res.offersIntern!.monthlySalary, @@ -187,60 +189,54 @@ export default function OfferProfile() { } } - return ( - <> - {getProfileQuery.isError && ( -
- + return getProfileQuery.isError ? ( +
+ +
+ ) : getProfileQuery.isLoading ? ( +
+
+ +
Loading...
+
+
+ ) : ( +
+
+
+
- )} - {getProfileQuery.isLoading && ( -
-
- -
Loading...
-
+
+
- )} - {!getProfileQuery.isLoading && !getProfileQuery.isError && ( -
-
-
- -
-
- -
-
-
- -
-
- )} - +
+
+ +
+
); } diff --git a/apps/portal/src/pages/offers/submit/result/[offerProfileId].tsx b/apps/portal/src/pages/offers/submit/result/[offerProfileId].tsx index f507283d..b8bde4a5 100644 --- a/apps/portal/src/pages/offers/submit/result/[offerProfileId].tsx +++ b/apps/portal/src/pages/offers/submit/result/[offerProfileId].tsx @@ -1,5 +1,6 @@ import Error from 'next/error'; import { useRouter } from 'next/router'; +import { useSession } from 'next-auth/react'; import { useEffect, useRef, useState } from 'react'; import { ArrowLeftIcon, ArrowRightIcon } from '@heroicons/react/20/solid'; import { EyeIcon } from '@heroicons/react/24/outline'; @@ -13,44 +14,43 @@ import OffersSubmissionAnalysis from '~/components/offers/offersSubmission/Offer import { getProfilePath } from '~/utils/offers/link'; import { trpc } from '~/utils/trpc'; -import type { ProfileAnalysis } from '~/types/offers'; - export default function OffersSubmissionResult() { const router = useRouter(); let { offerProfileId, token = '' } = router.query; offerProfileId = offerProfileId as string; token = token as string; const [step, setStep] = useState(0); - const [analysis, setAnalysis] = useState(null); - const [isValidToken, setIsValidToken] = useState(false); + const { data: session } = useSession(); const pageRef = useRef(null); const scrollToTop = () => pageRef.current?.scrollTo({ behavior: 'smooth', top: 0 }); - const checkToken = trpc.useQuery( - ['offers.profile.isValidToken', { profileId: offerProfileId, token }], - { - onSuccess(data) { - setIsValidToken(data); - }, - }, - ); + const checkToken = trpc.useQuery([ + 'offers.profile.isValidToken', + { profileId: offerProfileId, token }, + ]); - const getAnalysis = trpc.useQuery( - ['offers.analysis.get', { profileId: offerProfileId }], - { - onSuccess(data) { - setAnalysis(data); - }, - }, - ); + const getAnalysis = trpc.useQuery([ + 'offers.analysis.get', + { profileId: offerProfileId }, + ]); + + const isSavedQuery = trpc.useQuery([ + `offers.profile.isSaved`, + { profileId: offerProfileId, userId: session?.user?.id }, + ]); const steps = [ - , + , , @@ -77,71 +77,67 @@ export default function OffersSubmissionResult() { scrollToTop(); }, [step]); - return ( - <> - {(checkToken.isLoading || getAnalysis.isLoading) && ( -
-
- -
Loading...
+ return checkToken.isLoading || getAnalysis.isLoading ? ( +
+
+ +
Loading...
+
+
+ ) : checkToken.isError || getAnalysis.isError ? ( + + ) : checkToken.isSuccess && !checkToken.data ? ( + + ) : ( +
+
+
+
+
-
- )} - {checkToken.isSuccess && !isValidToken && ( - - )} - {getAnalysis.isSuccess && ( -
-
-
-
- + {steps[step]} + {step === 0 && ( +
+
-
- {steps[step]} - {step === 0 && ( -
-
- )} - {step === 1 && ( -
-
- )} + )} + {step === 1 && ( +
+
-
+ )}
- )} - +
+
); } diff --git a/apps/portal/src/utils/offers/time.tsx b/apps/portal/src/utils/offers/time.tsx index 5c7305dd..d61ef3a6 100644 --- a/apps/portal/src/utils/offers/time.tsx +++ b/apps/portal/src/utils/offers/time.tsx @@ -55,3 +55,18 @@ export function getCurrentYear() { export function convertToMonthYear(date: Date) { return { month: date.getMonth() + 1, year: date.getFullYear() } as MonthYear; } + +export function getDurationDisplayText(months: number) { + const years = Math.floor(months / 12); + const monthsRemainder = months % 12; + let durationDisplay = ''; + if (years > 0) { + durationDisplay = `${years} year${years > 1 ? 's' : ''}`; + } + if (monthsRemainder > 0) { + durationDisplay = durationDisplay.concat( + ` ${monthsRemainder} month${monthsRemainder > 1 ? 's' : ''}`, + ); + } + return durationDisplay; +}