mirror of
https://github.com/yangshun/tech-interview-handbook.git
synced 2025-08-02 02:52:40 +08:00
[offers][fix] Refactor UI (#500)
This commit is contained in:
@ -109,13 +109,13 @@ export default function OfferAnalysis({
|
||||
|
||||
return (
|
||||
<div>
|
||||
{isError && (
|
||||
{isError ? (
|
||||
<p className="m-10 text-center">
|
||||
An error occurred while generating profile analysis.
|
||||
</p>
|
||||
)}
|
||||
{isLoading && <Spinner className="m-10" display="block" size="lg" />}
|
||||
{!isError && !isLoading && (
|
||||
) : isLoading ? (
|
||||
<Spinner className="m-10" display="block" size="lg" />
|
||||
) : (
|
||||
<div>
|
||||
<Tabs
|
||||
label="Result Navigation"
|
||||
|
@ -33,15 +33,15 @@ export default function OfferProfileCard({
|
||||
location,
|
||||
title,
|
||||
previousCompanies,
|
||||
profileId,
|
||||
},
|
||||
}: OfferProfileCardProps) {
|
||||
return (
|
||||
// <a
|
||||
// className="my-5 block rounded-lg bg-white p-4 px-8 shadow-md"
|
||||
// href={`/offers/profile/${id}`}
|
||||
// rel="noreferrer"
|
||||
// target="_blank">
|
||||
<div className="my-5 block rounded-lg bg-white p-4 px-8 shadow-lg">
|
||||
<a
|
||||
className="my-5 block rounded-lg border bg-white p-4 px-8 shadow-md"
|
||||
href={`/offers/profile/${profileId}`}
|
||||
rel="noreferrer"
|
||||
target="_blank">
|
||||
<div className="flex items-center gap-x-5">
|
||||
<div>
|
||||
<ProfilePhotoHolder size="sm" />
|
||||
@ -82,6 +82,6 @@ export default function OfferProfileCard({
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</a>
|
||||
);
|
||||
}
|
||||
|
@ -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<boolean>;
|
||||
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({
|
||||
</p>
|
||||
<div className="mt-6">
|
||||
<Button
|
||||
disabled={isSavedQuery.isLoading || isSaved}
|
||||
disabled={isLoading || isSaved}
|
||||
icon={isSaved ? BookmarkSolidIcon : BookmarkOutlineIcon}
|
||||
isLoading={saveMutation.isLoading || isSavedQuery.isLoading}
|
||||
isLoading={saveMutation.isLoading}
|
||||
label={isSaved ? 'Added to account' : 'Add to your account'}
|
||||
size="sm"
|
||||
variant="secondary"
|
||||
|
@ -28,6 +28,7 @@ export default function OffersSubmissionAnalysis({
|
||||
allAnalysis={analysis}
|
||||
isError={isError}
|
||||
isLoading={isLoading}
|
||||
isSubmission={true}
|
||||
/>
|
||||
)}
|
||||
</div>
|
||||
|
@ -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<OffersProfileFormData> = 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 ? (
|
||||
<Spinner className="m-10" display="block" size="lg" />
|
||||
) : (
|
||||
<div ref={pageRef} className="w-full">
|
||||
<div className="flex justify-center">
|
||||
<div className="block w-full max-w-screen-md overflow-hidden rounded-lg sm:shadow-lg md:my-10">
|
||||
@ -324,9 +330,16 @@ export default function OffersSubmissionForm({
|
||||
}}
|
||||
/>
|
||||
<Button
|
||||
disabled={isSubmitting || isSubmitSuccessful}
|
||||
disabled={
|
||||
isSubmitting ||
|
||||
createOrUpdateMutation.isLoading ||
|
||||
generateAnalysisMutation.isLoading ||
|
||||
generateAnalysisMutation.isSuccess
|
||||
}
|
||||
icon={ArrowRightIcon}
|
||||
isLoading={isSubmitting || isSubmitSuccessful}
|
||||
isLoading={
|
||||
isSubmitting || createOrUpdateMutation.isLoading
|
||||
}
|
||||
label="Submit"
|
||||
type="submit"
|
||||
variant="primary"
|
||||
|
@ -279,23 +279,34 @@ function InternshipJobFields() {
|
||||
})}
|
||||
/>
|
||||
<Collapsible label="Add more details">
|
||||
<CitiesTypeahead
|
||||
label="Location"
|
||||
value={{
|
||||
id: watchCityId,
|
||||
label: watchCityName,
|
||||
value: watchCityId,
|
||||
}}
|
||||
onSelect={(option) => {
|
||||
if (option) {
|
||||
setValue('background.experiences.0.cityId', option.value);
|
||||
setValue('background.experiences.0.cityName', option.label);
|
||||
} else {
|
||||
setValue('background.experiences.0.cityId', '');
|
||||
setValue('background.experiences.0.cityName', '');
|
||||
}
|
||||
}}
|
||||
/>
|
||||
<div className="grid grid-cols-1 gap-6 sm:grid-cols-2">
|
||||
<CitiesTypeahead
|
||||
label="Location"
|
||||
value={{
|
||||
id: watchCityId,
|
||||
label: watchCityName,
|
||||
value: watchCityId,
|
||||
}}
|
||||
onSelect={(option) => {
|
||||
if (option) {
|
||||
setValue('background.experiences.0.cityId', option.value);
|
||||
setValue('background.experiences.0.cityName', option.label);
|
||||
} else {
|
||||
setValue('background.experiences.0.cityId', '');
|
||||
setValue('background.experiences.0.cityName', '');
|
||||
}
|
||||
}}
|
||||
/>
|
||||
<FormTextInput
|
||||
errorMessage={experiencesField?.durationInMonths?.message}
|
||||
label="Duration (months)"
|
||||
type="number"
|
||||
{...register(`background.experiences.0.durationInMonths`, {
|
||||
min: { message: FieldError.NON_NEGATIVE_NUMBER, value: 0 },
|
||||
valueAsNumber: true,
|
||||
})}
|
||||
/>
|
||||
</div>
|
||||
</Collapsible>
|
||||
</>
|
||||
);
|
||||
|
@ -3,11 +3,13 @@ import {
|
||||
BuildingOfficeIcon,
|
||||
MapPinIcon,
|
||||
} from '@heroicons/react/20/solid';
|
||||
import { JobType } from '@prisma/client';
|
||||
|
||||
import { JobTypeLabel } from '~/components/offers/constants';
|
||||
import type { OfferDisplayData } from '~/components/offers/types';
|
||||
|
||||
import { getLocationDisplayText } from '~/utils/offers/string';
|
||||
import { getDurationDisplayText } from '~/utils/offers/time';
|
||||
|
||||
type Props = Readonly<{
|
||||
offer: OfferDisplayData;
|
||||
@ -75,9 +77,9 @@ export default function OfferCard({
|
||||
<p>{receivedMonth}</p>
|
||||
</div>
|
||||
)}
|
||||
{duration && (
|
||||
{!!duration && (
|
||||
<div className="text-sm text-slate-500">
|
||||
<p>{`${duration} months`}</p>
|
||||
<p>{getDurationDisplayText(duration)}</p>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
@ -99,24 +101,27 @@ export default function OfferCard({
|
||||
return (
|
||||
<div className="border-t border-slate-200 px-4 py-5 sm:px-6">
|
||||
<dl className="grid grid-cols-2 gap-x-4 gap-y-8 sm:grid-cols-4">
|
||||
{totalCompensation && (
|
||||
<div className="col-span-1">
|
||||
<dt className="text-sm font-medium text-slate-500">
|
||||
Total Compensation
|
||||
</dt>
|
||||
<dd className="mt-1 text-sm text-slate-900">
|
||||
{totalCompensation}
|
||||
</dd>
|
||||
</div>
|
||||
)}
|
||||
{monthlySalary && (
|
||||
<div className="col-span-1">
|
||||
<dt className="text-sm font-medium text-slate-500">
|
||||
Monthly Salary
|
||||
</dt>
|
||||
<dd className="mt-1 text-sm text-slate-900">{monthlySalary}</dd>
|
||||
</div>
|
||||
)}
|
||||
{jobType === JobType.FULLTIME
|
||||
? totalCompensation && (
|
||||
<div className="col-span-1">
|
||||
<dt className="text-sm font-medium text-slate-500">
|
||||
Total Compensation
|
||||
</dt>
|
||||
<dd className="mt-1 text-sm text-slate-900">
|
||||
{totalCompensation}
|
||||
</dd>
|
||||
</div>
|
||||
)
|
||||
: monthlySalary && (
|
||||
<div className="col-span-1">
|
||||
<dt className="text-sm font-medium text-slate-500">
|
||||
Monthly Salary
|
||||
</dt>
|
||||
<dd className="mt-1 text-sm text-slate-900">
|
||||
{monthlySalary}
|
||||
</dd>
|
||||
</div>
|
||||
)}
|
||||
{base && (
|
||||
<div className="col-span-1">
|
||||
<dt className="text-sm font-medium text-slate-500">
|
||||
|
@ -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 && (
|
||||
<div className="flex w-full justify-center">
|
||||
<Error statusCode={404} title="Requested profile does not exist" />
|
||||
return getProfileQuery.isError ? (
|
||||
<div className="flex w-full justify-center">
|
||||
<Error statusCode={404} title="Requested profile does not exist." />
|
||||
</div>
|
||||
) : getProfileQuery.isLoading ? (
|
||||
<div className="flex h-screen w-screen">
|
||||
<div className="m-auto mx-auto w-screen justify-center font-medium text-slate-500">
|
||||
<Spinner display="block" size="lg" />
|
||||
<div className="text-center">Loading...</div>
|
||||
</div>
|
||||
</div>
|
||||
) : (
|
||||
<div className="w-full divide-x lg:flex">
|
||||
<div className="divide-y lg:w-2/3">
|
||||
<div className="h-fit">
|
||||
<ProfileHeader
|
||||
background={background}
|
||||
handleDelete={handleDelete}
|
||||
isEditable={isEditable}
|
||||
isLoading={getProfileQuery.isLoading}
|
||||
selectedTab={selectedTab}
|
||||
setSelectedTab={setSelectedTab}
|
||||
/>
|
||||
</div>
|
||||
)}
|
||||
{getProfileQuery.isLoading && (
|
||||
<div className="flex h-screen w-screen">
|
||||
<div className="m-auto mx-auto w-screen justify-center font-medium text-slate-500">
|
||||
<Spinner display="block" size="lg" />
|
||||
<div className="text-center">Loading...</div>
|
||||
</div>
|
||||
<div>
|
||||
<ProfileDetails
|
||||
analysis={analysis}
|
||||
background={background}
|
||||
isEditable={isEditable}
|
||||
isLoading={getProfileQuery.isLoading}
|
||||
offers={offers}
|
||||
profileId={offerProfileId as string}
|
||||
selectedTab={selectedTab}
|
||||
/>
|
||||
</div>
|
||||
)}
|
||||
{!getProfileQuery.isLoading && !getProfileQuery.isError && (
|
||||
<div className="w-full divide-x lg:flex">
|
||||
<div className="divide-y lg:w-2/3">
|
||||
<div className="h-fit">
|
||||
<ProfileHeader
|
||||
background={background}
|
||||
handleDelete={handleDelete}
|
||||
isEditable={isEditable}
|
||||
isLoading={getProfileQuery.isLoading}
|
||||
selectedTab={selectedTab}
|
||||
setSelectedTab={setSelectedTab}
|
||||
/>
|
||||
</div>
|
||||
<div>
|
||||
<ProfileDetails
|
||||
analysis={analysis}
|
||||
background={background}
|
||||
isEditable={isEditable}
|
||||
isLoading={getProfileQuery.isLoading}
|
||||
offers={offers}
|
||||
profileId={offerProfileId as string}
|
||||
selectedTab={selectedTab}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<div
|
||||
className="bg-white lg:fixed lg:right-0 lg:bottom-0 lg:w-1/3"
|
||||
style={{ top: 64 }}>
|
||||
<ProfileComments
|
||||
isDisabled={deleteMutation.isLoading}
|
||||
isEditable={isEditable}
|
||||
isLoading={getProfileQuery.isLoading}
|
||||
profileId={offerProfileId as string}
|
||||
profileName={background?.profileName}
|
||||
token={token as string}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
</>
|
||||
</div>
|
||||
<div
|
||||
className="bg-white lg:fixed lg:right-0 lg:bottom-0 lg:w-1/3"
|
||||
style={{ top: 64 }}>
|
||||
<ProfileComments
|
||||
isDisabled={deleteMutation.isLoading}
|
||||
isEditable={isEditable}
|
||||
isLoading={getProfileQuery.isLoading}
|
||||
profileId={offerProfileId as string}
|
||||
profileName={background?.profileName}
|
||||
token={token as string}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
@ -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<ProfileAnalysis | null>(null);
|
||||
const [isValidToken, setIsValidToken] = useState(false);
|
||||
const { data: session } = useSession();
|
||||
|
||||
const pageRef = useRef<HTMLDivElement>(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 = [
|
||||
<OffersProfileSave key={0} profileId={offerProfileId} token={token} />,
|
||||
<OffersProfileSave
|
||||
key={0}
|
||||
isSavedQuery={isSavedQuery}
|
||||
profileId={offerProfileId}
|
||||
token={token}
|
||||
/>,
|
||||
<OffersSubmissionAnalysis
|
||||
key={1}
|
||||
analysis={analysis}
|
||||
analysis={getAnalysis.data}
|
||||
isError={getAnalysis.isError}
|
||||
isLoading={getAnalysis.isLoading}
|
||||
/>,
|
||||
@ -77,71 +77,67 @@ export default function OffersSubmissionResult() {
|
||||
scrollToTop();
|
||||
}, [step]);
|
||||
|
||||
return (
|
||||
<>
|
||||
{(checkToken.isLoading || getAnalysis.isLoading) && (
|
||||
<div className="flex h-screen w-screen">
|
||||
<div className="m-auto mx-auto w-screen justify-center font-medium text-slate-500">
|
||||
<Spinner display="block" size="lg" />
|
||||
<div className="text-center">Loading...</div>
|
||||
return checkToken.isLoading || getAnalysis.isLoading ? (
|
||||
<div className="flex h-screen w-screen">
|
||||
<div className="m-auto mx-auto w-screen justify-center font-medium text-slate-500">
|
||||
<Spinner display="block" size="lg" />
|
||||
<div className="text-center">Loading...</div>
|
||||
</div>
|
||||
</div>
|
||||
) : checkToken.isError || getAnalysis.isError ? (
|
||||
<Error statusCode={404} title="Error loading page" />
|
||||
) : checkToken.isSuccess && !checkToken.data ? (
|
||||
<Error
|
||||
statusCode={403}
|
||||
title="You do not have permissions to access this page"
|
||||
/>
|
||||
) : (
|
||||
<div ref={pageRef} className="w-full">
|
||||
<div className="flex justify-center">
|
||||
<div className="block w-full max-w-screen-md overflow-hidden rounded-lg sm:shadow-lg md:my-10">
|
||||
<div className="flex justify-center bg-slate-100 px-4 py-4 sm:px-6 lg:px-8">
|
||||
<Breadcrumbs
|
||||
currentStep={step}
|
||||
setStep={setStep}
|
||||
steps={breadcrumbSteps}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
{checkToken.isSuccess && !isValidToken && (
|
||||
<Error
|
||||
statusCode={403}
|
||||
title="You do not have permissions to access this page"
|
||||
/>
|
||||
)}
|
||||
{getAnalysis.isSuccess && (
|
||||
<div ref={pageRef} className="w-full">
|
||||
<div className="flex justify-center">
|
||||
<div className="block w-full max-w-screen-md overflow-hidden rounded-lg sm:shadow-lg md:my-10">
|
||||
<div className="flex justify-center bg-slate-100 px-4 py-4 sm:px-6 lg:px-8">
|
||||
<Breadcrumbs
|
||||
currentStep={step}
|
||||
setStep={setStep}
|
||||
steps={breadcrumbSteps}
|
||||
<div className="bg-white p-6 sm:p-10">
|
||||
{steps[step]}
|
||||
{step === 0 && (
|
||||
<div className="flex justify-end">
|
||||
<Button
|
||||
disabled={false}
|
||||
icon={ArrowRightIcon}
|
||||
label="Next"
|
||||
variant="primary"
|
||||
onClick={() => setStep(step + 1)}
|
||||
/>
|
||||
</div>
|
||||
<div className="bg-white p-6 sm:p-10">
|
||||
{steps[step]}
|
||||
{step === 0 && (
|
||||
<div className="flex justify-end">
|
||||
<Button
|
||||
disabled={false}
|
||||
icon={ArrowRightIcon}
|
||||
label="Next"
|
||||
variant="primary"
|
||||
onClick={() => setStep(step + 1)}
|
||||
/>
|
||||
</div>
|
||||
)}
|
||||
{step === 1 && (
|
||||
<div className="flex items-center justify-between">
|
||||
<Button
|
||||
addonPosition="start"
|
||||
icon={ArrowLeftIcon}
|
||||
label="Previous"
|
||||
variant="secondary"
|
||||
onClick={() => setStep(step - 1)}
|
||||
/>
|
||||
<Button
|
||||
href={getProfilePath(
|
||||
offerProfileId as string,
|
||||
token as string,
|
||||
)}
|
||||
icon={EyeIcon}
|
||||
label="View your profile"
|
||||
variant="primary"
|
||||
/>
|
||||
</div>
|
||||
)}
|
||||
)}
|
||||
{step === 1 && (
|
||||
<div className="flex items-center justify-between">
|
||||
<Button
|
||||
addonPosition="start"
|
||||
icon={ArrowLeftIcon}
|
||||
label="Previous"
|
||||
variant="secondary"
|
||||
onClick={() => setStep(step - 1)}
|
||||
/>
|
||||
<Button
|
||||
href={getProfilePath(
|
||||
offerProfileId as string,
|
||||
token as string,
|
||||
)}
|
||||
icon={EyeIcon}
|
||||
label="View your profile"
|
||||
variant="primary"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
</>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
@ -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;
|
||||
}
|
||||
|
Reference in New Issue
Block a user