diff --git a/apps/portal/src/components/offers/Breadcrumb.tsx b/apps/portal/src/components/offers/Breadcrumb.tsx index 6a802522..30eb07b4 100644 --- a/apps/portal/src/components/offers/Breadcrumb.tsx +++ b/apps/portal/src/components/offers/Breadcrumb.tsx @@ -1,21 +1,43 @@ +export type BreadcrumbStep = { + label: string; + step?: number; +}; + type BreadcrumbsProps = Readonly<{ currentStep: number; - stepLabels: Array; + setStep: (nextStep: number) => void; + steps: Array; }>; -export function Breadcrumbs({ stepLabels, currentStep }: BreadcrumbsProps) { +function getPrimaryText(text: string) { + return

{text}

; +} + +function getSlateText(text: string) { + return

{text}

; +} + +function getTextWithLink(text: string, onClickHandler: () => void) { + return ( +

+ {text} +

+ ); +} + +export function Breadcrumbs({ steps, currentStep, setStep }: BreadcrumbsProps) { return (
- {stepLabels.map((label, index) => ( + {steps.map(({ label, step }, index) => (
- {index === currentStep ? ( -

{label}

- ) : ( -

{label}

- )} - {index !== stepLabels.length - 1 && ( -

{'>'}

- )} + {step === currentStep + ? getPrimaryText(label) + : step !== undefined + ? getTextWithLink(label, () => setStep(step)) + : getSlateText(label)} + {index !== steps.length - 1 && getSlateText('>')}
))}
diff --git a/apps/portal/src/components/offers/offersSubmission/OffersSubmissionAnalysis.tsx b/apps/portal/src/components/offers/offersSubmission/OffersSubmissionAnalysis.tsx index 325cbae0..d5aee9b0 100644 --- a/apps/portal/src/components/offers/offersSubmission/OffersSubmissionAnalysis.tsx +++ b/apps/portal/src/components/offers/offersSubmission/OffersSubmissionAnalysis.tsx @@ -1,32 +1,19 @@ -import { useRouter } from 'next/router'; -import { EyeIcon } from '@heroicons/react/24/outline'; - -import { Button } from '~/../../../packages/ui/dist'; -import { getProfilePath } from '~/utils/offers/link'; - import OfferAnalysis from '../offerAnalysis/OfferAnalysis'; import type { ProfileAnalysis } from '~/types/offers'; - type Props = Readonly<{ analysis?: ProfileAnalysis | null; isError: boolean; isLoading: boolean; - profileId?: string; - token?: string; }>; export default function OffersSubmissionAnalysis({ analysis, isError, isLoading, - profileId = '', - token = '', }: Props) { - const router = useRouter(); - return ( -
+
Result
@@ -36,14 +23,6 @@ export default function OffersSubmissionAnalysis({ isError={isError} isLoading={isLoading} /> -
-
); } diff --git a/apps/portal/src/components/offers/offersSubmission/OffersSubmissionForm.tsx b/apps/portal/src/components/offers/offersSubmission/OffersSubmissionForm.tsx index 55f2f756..2e9c90aa 100644 --- a/apps/portal/src/components/offers/offersSubmission/OffersSubmissionForm.tsx +++ b/apps/portal/src/components/offers/offersSubmission/OffersSubmissionForm.tsx @@ -1,12 +1,13 @@ -import { useRef, useState } from 'react'; +import { useRouter } from 'next/router'; +import { useEffect, useRef, useState } from 'react'; 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 } from '@tih/ui'; +import type { BreadcrumbStep } from '~/components/offers/Breadcrumb'; import { Breadcrumbs } from '~/components/offers/Breadcrumb'; -import OffersProfileSave from '~/components/offers/offersSubmission/OffersProfileSave'; import BackgroundForm from '~/components/offers/offersSubmission/submissionForm/BackgroundForm'; import OfferDetailsForm from '~/components/offers/offersSubmission/submissionForm/OfferDetailsForm'; import type { @@ -23,10 +24,6 @@ import { import { getCurrentMonth, getCurrentYear } from '~/utils/offers/time'; import { trpc } from '~/utils/trpc'; -import OffersSubmissionAnalysis from './OffersSubmissionAnalysis'; - -import type { ProfileAnalysis } from '~/types/offers'; - const defaultOfferValues = { comments: '', companyId: '', @@ -59,13 +56,6 @@ const defaultOfferProfileValues = { offers: [defaultOfferValues], }; -type FormStep = { - component: JSX.Element; - hasNext: boolean; - hasPrevious: boolean; - label: string; -}; - type Props = Readonly<{ initialOfferProfileValues?: OffersProfileFormData; profileId?: string; @@ -77,11 +67,14 @@ export default function OffersSubmissionForm({ profileId: editProfileId = '', token: editToken = '', }: Props) { - const [formStep, setFormStep] = useState(0); - const [profileId, setProfileId] = useState(editProfileId); - const [token, setToken] = useState(editToken); - const [analysis, setAnalysis] = useState(null); + const [step, setStep] = useState(0); + const [params, setParams] = useState({ + profileId: editProfileId, + token: editToken, + }); + const [isSubmitted, setIsSubmitted] = useState(false); + const router = useRouter(); const pageRef = useRef(null); const scrollToTop = () => pageRef.current?.scrollTo({ behavior: 'smooth', top: 0 }); @@ -97,87 +90,61 @@ export default function OffersSubmissionForm({ onError(error) { console.error(error.message); }, - onSuccess(data) { - setAnalysis(data); + onSuccess() { + router.push( + `/offers/submit/result/${params.profileId}?token=${params.token}`, + ); }, }, ); - const formSteps: Array = [ + const steps = [ + , + , + ]; + + const breadcrumbSteps: Array = [ { - component: ( - - ), - hasNext: true, - hasPrevious: false, label: 'Offers', + step: 0, }, { - component: , - hasNext: false, - hasPrevious: true, label: 'Background', + step: 1, }, { - component: ( - - ), - hasNext: true, - hasPrevious: false, label: 'Save profile', }, { - component: ( - - ), - hasNext: false, - hasPrevious: true, label: 'Analysis', }, ]; - const formStepsLabels = formSteps.map((step) => step.label); - - const nextStep = async (currStep: number) => { + const goToNextStep = async (currStep: number) => { if (currStep === 0) { const result = await trigger('offers'); if (!result) { return; } } - setFormStep(formStep + 1); - scrollToTop(); - }; - - const previousStep = () => { - setFormStep(formStep - 1); - scrollToTop(); + setStep(step + 1); }; const mutationpath = - profileId && token ? 'offers.profile.update' : 'offers.profile.create'; + editProfileId && editToken + ? 'offers.profile.update' + : 'offers.profile.create'; const createOrUpdateMutation = trpc.useMutation([mutationpath], { onError(error) { console.error(error.message); }, onSuccess(data) { - generateAnalysisMutation.mutate({ - profileId: data?.id || '', - }); - setProfileId(data.id); - setToken(data.token); - setFormStep(formStep + 1); - scrollToTop(); + setParams({ profileId: data.id, token: data.token }); + setIsSubmitted(true); }, }); @@ -206,47 +173,64 @@ export default function OffersSubmissionForm({ ), })); - if (profileId && token) { + if (params.profileId && params.token) { createOrUpdateMutation.mutate({ background, - id: profileId, + id: params.profileId, offers, - token, + token: params.token, }); } else { createOrUpdateMutation.mutate({ background, offers }); } }; + useEffect(() => { + if (isSubmitted) { + generateAnalysisMutation.mutate({ + profileId: params.profileId, + }); + } + // eslint-disable-next-line react-hooks/exhaustive-deps + }, [isSubmitted, params]); + + useEffect(() => { + scrollToTop(); + }, [step]); + return (
- +
- {formSteps[formStep].component} + {steps[step]} {/*
{JSON.stringify(formMethods.watch(), null, 2)}
*/} - {formSteps[formStep].hasNext && ( + {step === 0 && (
)} - {formStep === 1 && ( + {step === 1 && (
diff --git a/apps/portal/src/pages/offers/profile/[offerProfileId].tsx b/apps/portal/src/pages/offers/profile/[offerProfileId].tsx index 7cf2811d..77229cc1 100644 --- a/apps/portal/src/pages/offers/profile/[offerProfileId].tsx +++ b/apps/portal/src/pages/offers/profile/[offerProfileId].tsx @@ -50,11 +50,11 @@ export default function OfferProfile() { router.push(HOME_URL); } // If the profile is not editable with a wrong token, redirect to the profile page - if (!data?.isEditable && token !== '') { + if (!data.isEditable && token !== '') { router.push(getProfilePath(offerProfileId as string)); } - setIsEditable(data?.isEditable ?? false); + setIsEditable(data.isEditable); const filteredOffers: Array = data ? data?.offers.map((res: ProfileOffer) => { diff --git a/apps/portal/src/pages/offers/submit/index.tsx b/apps/portal/src/pages/offers/submit/index.tsx new file mode 100644 index 00000000..df2015f1 --- /dev/null +++ b/apps/portal/src/pages/offers/submit/index.tsx @@ -0,0 +1,5 @@ +import OffersSubmissionForm from '~/components/offers/offersSubmission/OffersSubmissionForm'; + +export default function OffersSubmissionPage() { + return ; +} diff --git a/apps/portal/src/pages/offers/submit/result/[offerProfileId].tsx b/apps/portal/src/pages/offers/submit/result/[offerProfileId].tsx new file mode 100644 index 00000000..dd379145 --- /dev/null +++ b/apps/portal/src/pages/offers/submit/result/[offerProfileId].tsx @@ -0,0 +1,123 @@ +import { useRouter } from 'next/router'; +import { useEffect, useRef, useState } from 'react'; +import { ArrowLeftIcon, ArrowRightIcon } from '@heroicons/react/20/solid'; +import { EyeIcon } from '@heroicons/react/24/outline'; +import { Button, Spinner } from '@tih/ui'; + +import type { BreadcrumbStep } from '~/components/offers/Breadcrumb'; +import { Breadcrumbs } from '~/components/offers/Breadcrumb'; +import OffersProfileSave from '~/components/offers/offersSubmission/OffersProfileSave'; +import OffersSubmissionAnalysis from '~/components/offers/offersSubmission/OffersSubmissionAnalysis'; + +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 pageRef = useRef(null); + const scrollToTop = () => + pageRef.current?.scrollTo({ behavior: 'smooth', top: 0 }); + + // TODO: Check if the token is valid before showing this page + const getAnalysis = trpc.useQuery( + ['offers.analysis.get', { profileId: offerProfileId }], + { + onSuccess(data) { + setAnalysis(data); + }, + }, + ); + + const steps = [ + , + , + ]; + + const breadcrumbSteps: Array = [ + { + label: 'Offers', + }, + { + label: 'Background', + }, + { + label: 'Save profile', + step: 0, + }, + { + label: 'Analysis', + step: 1, + }, + ]; + + useEffect(() => { + scrollToTop(); + }, [step]); + + return ( + <> + {getAnalysis.isLoading && ( + + )} + {!getAnalysis.isLoading && ( +
+
+
+
+ +
+ {steps[step]} + {step === 0 && ( +
+
+ )} + {step === 1 && ( +
+
+ )} +
+
+
+ )} + + ); +}