mirror of
https://github.com/yangshun/tech-interview-handbook.git
synced 2025-07-27 20:22:33 +08:00
[offers][fix] Fix offers UI (#460)
This commit is contained in:
@ -19,12 +19,14 @@ type OfferAnalysisData = {
|
||||
|
||||
type OfferAnalysisContentProps = Readonly<{
|
||||
analysis: OfferAnalysisData;
|
||||
isSubmission: boolean;
|
||||
tab: string;
|
||||
}>;
|
||||
|
||||
function OfferAnalysisContent({
|
||||
analysis: { offer, offerAnalysis },
|
||||
tab,
|
||||
isSubmission,
|
||||
}: OfferAnalysisContentProps) {
|
||||
if (!offerAnalysis || !offer || offerAnalysis.noOfOffers === 0) {
|
||||
if (tab === OVERALL_TAB) {
|
||||
@ -46,16 +48,30 @@ function OfferAnalysisContent({
|
||||
<>
|
||||
<OfferPercentileAnalysisText
|
||||
companyName={offer.company.name}
|
||||
isSubmission={isSubmission}
|
||||
offerAnalysis={offerAnalysis}
|
||||
tab={tab}
|
||||
/>
|
||||
<p className="mt-5">Here are some of the top offers relevant to you:</p>
|
||||
<p className="mt-5">
|
||||
{isSubmission
|
||||
? 'Here are some of the top offers relevant to you:'
|
||||
: 'Relevant top offers:'}
|
||||
</p>
|
||||
{offerAnalysis.topPercentileOffers.map((topPercentileOffer) => (
|
||||
<OfferProfileCard
|
||||
key={topPercentileOffer.id}
|
||||
offerProfile={topPercentileOffer}
|
||||
/>
|
||||
))}
|
||||
{/* {offerAnalysis.topPercentileOffers.length > 0 && (
|
||||
<div className="mb-4 flex justify-end">
|
||||
<Button
|
||||
icon={EllipsisHorizontalIcon}
|
||||
label="View more offers"
|
||||
variant="tertiary"
|
||||
/>
|
||||
</div>
|
||||
)} */}
|
||||
</>
|
||||
);
|
||||
}
|
||||
@ -64,12 +80,14 @@ type OfferAnalysisProps = Readonly<{
|
||||
allAnalysis?: ProfileAnalysis | null;
|
||||
isError: boolean;
|
||||
isLoading: boolean;
|
||||
isSubmission?: boolean;
|
||||
}>;
|
||||
|
||||
export default function OfferAnalysis({
|
||||
allAnalysis,
|
||||
isError,
|
||||
isLoading,
|
||||
isSubmission = false,
|
||||
}: OfferAnalysisProps) {
|
||||
const [tab, setTab] = useState(OVERALL_TAB);
|
||||
const [analysis, setAnalysis] = useState<OfferAnalysisData | null>(null);
|
||||
@ -117,7 +135,11 @@ export default function OfferAnalysis({
|
||||
onChange={setTab}
|
||||
/>
|
||||
<HorizontalDivider className="mb-5" />
|
||||
<OfferAnalysisContent analysis={analysis} tab={tab} />
|
||||
<OfferAnalysisContent
|
||||
analysis={analysis}
|
||||
isSubmission={isSubmission}
|
||||
tab={tab}
|
||||
/>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
|
@ -4,6 +4,7 @@ import type { Analysis } from '~/types/offers';
|
||||
|
||||
type OfferPercentileAnalysisTextProps = Readonly<{
|
||||
companyName: string;
|
||||
isSubmission: boolean;
|
||||
offerAnalysis: Analysis;
|
||||
tab: string;
|
||||
}>;
|
||||
@ -12,18 +13,21 @@ export default function OfferPercentileAnalysisText({
|
||||
tab,
|
||||
companyName,
|
||||
offerAnalysis: { noOfOffers, percentile },
|
||||
isSubmission,
|
||||
}: OfferPercentileAnalysisTextProps) {
|
||||
return tab === OVERALL_TAB ? (
|
||||
<p>
|
||||
Your highest offer is from <b>{companyName}</b>, which is{' '}
|
||||
<b>{percentile.toFixed(1)}</b> percentile out of <b>{noOfOffers}</b>{' '}
|
||||
offers received for the same job title and YOE(±1) in the last year.
|
||||
{isSubmission ? 'Your' : "This profile's"} highest offer is from{' '}
|
||||
<b>{companyName}</b>, which is <b>{percentile.toFixed(1)}</b> percentile
|
||||
out of <b>{noOfOffers}</b> offers received for the same job title and
|
||||
YOE(±1) in the last year.
|
||||
</p>
|
||||
) : (
|
||||
<p>
|
||||
Your offer from <b>{companyName}</b> is <b>{percentile.toFixed(1)}</b>{' '}
|
||||
percentile out of <b>{noOfOffers}</b> offers received in {companyName} for
|
||||
the same job title and YOE(±1) in the last year.
|
||||
{isSubmission ? 'Your' : 'The'} offer from <b>{companyName}</b> is{' '}
|
||||
<b>{percentile.toFixed(1)}</b> percentile out of <b>{noOfOffers}</b>{' '}
|
||||
offers received in {companyName} for the same job title and YOE(±1) in the
|
||||
last year.
|
||||
</p>
|
||||
);
|
||||
}
|
||||
|
@ -12,6 +12,7 @@ import { convertMoneyToString } from '~/utils/offers/currency';
|
||||
import { formatDate } from '~/utils/offers/time';
|
||||
|
||||
import ProfilePhotoHolder from '../profile/ProfilePhotoHolder';
|
||||
import { JobTypeLabel } from '../types';
|
||||
|
||||
import type { AnalysisOffer } from '~/types/offers';
|
||||
|
||||
@ -34,7 +35,12 @@ export default function OfferProfileCard({
|
||||
},
|
||||
}: OfferProfileCardProps) {
|
||||
return (
|
||||
<div className="my-5 block rounded-lg bg-white p-4 px-8 shadow-md">
|
||||
// <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">
|
||||
<div className="flex items-center gap-x-5">
|
||||
<div>
|
||||
<ProfilePhotoHolder size="sm" />
|
||||
@ -58,7 +64,8 @@ export default function OfferProfileCard({
|
||||
<div className="flex items-end justify-between">
|
||||
<div className="col-span-1 row-span-3">
|
||||
<p className="font-bold">
|
||||
{getLabelForJobTitleType(title as JobTitleType)}
|
||||
{getLabelForJobTitleType(title as JobTitleType)}{' '}
|
||||
{`(${JobTypeLabel[jobType]})`}
|
||||
</p>
|
||||
<p>
|
||||
Company: {company.name}, {location}
|
||||
|
@ -22,6 +22,7 @@ export default function OffersSubmissionAnalysis({
|
||||
allAnalysis={analysis}
|
||||
isError={isError}
|
||||
isLoading={isLoading}
|
||||
isSubmission={true}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
|
@ -27,6 +27,7 @@ import { trpc } from '~/utils/trpc';
|
||||
const defaultOfferValues = {
|
||||
comments: '',
|
||||
companyId: '',
|
||||
jobTitle: '',
|
||||
jobType: JobType.FULLTIME,
|
||||
location: '',
|
||||
monthYearReceived: {
|
||||
@ -39,11 +40,38 @@ const defaultOfferValues = {
|
||||
export const defaultFullTimeOfferValues = {
|
||||
...defaultOfferValues,
|
||||
jobType: JobType.FULLTIME,
|
||||
offersFullTime: {
|
||||
baseSalary: {
|
||||
currency: 'SGD',
|
||||
value: null,
|
||||
},
|
||||
bonus: {
|
||||
currency: 'SGD',
|
||||
value: null,
|
||||
},
|
||||
level: '',
|
||||
stocks: {
|
||||
currency: 'SGD',
|
||||
value: null,
|
||||
},
|
||||
totalCompensation: {
|
||||
currency: 'SGD',
|
||||
value: null,
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
export const defaultInternshipOfferValues = {
|
||||
...defaultOfferValues,
|
||||
jobType: JobType.INTERN,
|
||||
offersIntern: {
|
||||
internshipCycle: null,
|
||||
monthlySalary: {
|
||||
currency: 'SGD',
|
||||
value: null,
|
||||
},
|
||||
startYear: null,
|
||||
},
|
||||
};
|
||||
|
||||
const defaultOfferProfileValues = {
|
||||
@ -198,6 +226,32 @@ export default function OffersSubmissionForm({
|
||||
scrollToTop();
|
||||
}, [step]);
|
||||
|
||||
useEffect(() => {
|
||||
const warningText =
|
||||
'Leave this page? Changes that you made will not be saved.';
|
||||
const handleWindowClose = (e: BeforeUnloadEvent) => {
|
||||
e.preventDefault();
|
||||
return (e.returnValue = warningText);
|
||||
};
|
||||
const handleRouteChange = (url: string) => {
|
||||
if (url.includes('/offers/submit/result')) {
|
||||
return;
|
||||
}
|
||||
if (window.confirm(warningText)) {
|
||||
return;
|
||||
}
|
||||
router.events.emit('routeChangeError');
|
||||
throw 'routeChange aborted.';
|
||||
};
|
||||
window.addEventListener('beforeunload', handleWindowClose);
|
||||
router.events.on('routeChangeStart', handleRouteChange);
|
||||
return () => {
|
||||
window.removeEventListener('beforeunload', handleWindowClose);
|
||||
router.events.off('routeChangeStart', handleRouteChange);
|
||||
};
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
}, []);
|
||||
|
||||
return (
|
||||
<div ref={pageRef} className="fixed h-full w-full overflow-y-scroll">
|
||||
<div className="mb-20 flex justify-center">
|
||||
@ -210,7 +264,7 @@ export default function OffersSubmissionForm({
|
||||
/>
|
||||
</div>
|
||||
<FormProvider {...formMethods}>
|
||||
<form onSubmit={handleSubmit(onSubmit)}>
|
||||
<form className="text-sm" onSubmit={handleSubmit(onSubmit)}>
|
||||
{steps[step]}
|
||||
{/* <pre>{JSON.stringify(formMethods.watch(), null, 2)}</pre> */}
|
||||
{step === 0 && (
|
||||
|
@ -11,6 +11,8 @@ import {
|
||||
} from '~/components/offers/constants';
|
||||
import type { BackgroundPostData } from '~/components/offers/types';
|
||||
import CompaniesTypeahead from '~/components/shared/CompaniesTypeahead';
|
||||
import type { JobTitleType } from '~/components/shared/JobTitles';
|
||||
import { getLabelForJobTitleType } from '~/components/shared/JobTitles';
|
||||
import JobTitlesTypeahead from '~/components/shared/JobTitlesTypahead';
|
||||
|
||||
import {
|
||||
@ -92,23 +94,47 @@ function FullTimeJobFields() {
|
||||
background: BackgroundPostData;
|
||||
}>();
|
||||
const experiencesField = formState.errors.background?.experiences?.[0];
|
||||
|
||||
const watchJobTitle = useWatch({
|
||||
name: 'background.experiences.0.title',
|
||||
});
|
||||
const watchCompanyId = useWatch({
|
||||
name: 'background.experiences.0.companyId',
|
||||
});
|
||||
const watchCompanyName = useWatch({
|
||||
name: 'background.experiences.0.companyName',
|
||||
});
|
||||
|
||||
return (
|
||||
<>
|
||||
<div className="mb-5 grid grid-cols-2 space-x-3">
|
||||
<div>
|
||||
<JobTitlesTypeahead
|
||||
// @ts-ignore TODO(offers): handle potentially null value.
|
||||
onSelect={({ value }) =>
|
||||
setValue(`background.experiences.0.title`, value)
|
||||
}
|
||||
value={{
|
||||
id: watchJobTitle,
|
||||
label: getLabelForJobTitleType(watchJobTitle as JobTitleType),
|
||||
value: watchJobTitle,
|
||||
}}
|
||||
onSelect={(option) => {
|
||||
if (option) {
|
||||
setValue('background.experiences.0.title', option.value);
|
||||
}
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
<div>
|
||||
<CompaniesTypeahead
|
||||
// @ts-ignore TODO(offers): handle potentially null value.
|
||||
onSelect={({ value }) =>
|
||||
setValue(`background.experiences.0.companyId`, value)
|
||||
}
|
||||
value={{
|
||||
id: watchCompanyId,
|
||||
label: watchCompanyName,
|
||||
value: watchCompanyId,
|
||||
}}
|
||||
onSelect={(option) => {
|
||||
if (option) {
|
||||
setValue('background.experiences.0.companyId', option.value);
|
||||
setValue('background.experiences.0.companyName', option.label);
|
||||
}
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
@ -175,23 +201,46 @@ function InternshipJobFields() {
|
||||
}>();
|
||||
const experiencesField = formState.errors.background?.experiences?.[0];
|
||||
|
||||
const watchJobTitle = useWatch({
|
||||
name: 'background.experiences.0.title',
|
||||
});
|
||||
const watchCompanyId = useWatch({
|
||||
name: 'background.experiences.0.companyId',
|
||||
});
|
||||
const watchCompanyName = useWatch({
|
||||
name: 'background.experiences.0.companyName',
|
||||
});
|
||||
|
||||
return (
|
||||
<>
|
||||
<div className="mb-5 grid grid-cols-2 space-x-3">
|
||||
<div>
|
||||
<JobTitlesTypeahead
|
||||
// @ts-ignore TODO(offers): handle potentially null value.
|
||||
onSelect={({ value }) =>
|
||||
setValue(`background.experiences.0.title`, value)
|
||||
}
|
||||
value={{
|
||||
id: watchJobTitle,
|
||||
label: getLabelForJobTitleType(watchJobTitle as JobTitleType),
|
||||
value: watchJobTitle,
|
||||
}}
|
||||
onSelect={(option) => {
|
||||
if (option) {
|
||||
setValue('background.experiences.0.title', option.value);
|
||||
}
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
<div>
|
||||
<CompaniesTypeahead
|
||||
// @ts-ignore TODO(offers): handle potentially null value.
|
||||
onSelect={({ value }) =>
|
||||
setValue(`background.experiences.0.companyId`, value)
|
||||
}
|
||||
value={{
|
||||
id: watchCompanyId,
|
||||
label: watchCompanyName,
|
||||
value: watchCompanyId,
|
||||
}}
|
||||
onSelect={(option) => {
|
||||
if (option) {
|
||||
setValue('background.experiences.0.companyId', option.value);
|
||||
setValue('background.experiences.0.companyName', option.label);
|
||||
}
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
@ -13,6 +13,8 @@ import { JobType } from '@prisma/client';
|
||||
import { Button, Dialog } from '@tih/ui';
|
||||
|
||||
import CompaniesTypeahead from '~/components/shared/CompaniesTypeahead';
|
||||
import type { JobTitleType } from '~/components/shared/JobTitles';
|
||||
import { getLabelForJobTitleType } from '~/components/shared/JobTitles';
|
||||
import JobTitlesTypeahead from '~/components/shared/JobTitlesTypahead';
|
||||
|
||||
import {
|
||||
@ -51,6 +53,15 @@ function FullTimeOfferDetailsForm({
|
||||
}>();
|
||||
const offerFields = formState.errors.offers?.[index];
|
||||
|
||||
const watchJobTitle = useWatch({
|
||||
name: `offers.${index}.offersFullTime.title`,
|
||||
});
|
||||
const watchCompanyId = useWatch({
|
||||
name: `offers.${index}.companyId`,
|
||||
});
|
||||
const watchCompanyName = useWatch({
|
||||
name: `offers.${index}.companyName`,
|
||||
});
|
||||
const watchCurrency = useWatch({
|
||||
name: `offers.${index}.offersFullTime.totalCompensation.currency`,
|
||||
});
|
||||
@ -70,10 +81,16 @@ function FullTimeOfferDetailsForm({
|
||||
<div>
|
||||
<JobTitlesTypeahead
|
||||
required={true}
|
||||
// @ts-ignore TODO(offers): handle potentially null value.
|
||||
onSelect={({ value }) =>
|
||||
setValue(`offers.${index}.offersFullTime.title`, value)
|
||||
}
|
||||
value={{
|
||||
id: watchJobTitle,
|
||||
label: getLabelForJobTitleType(watchJobTitle as JobTitleType),
|
||||
value: watchJobTitle,
|
||||
}}
|
||||
onSelect={(option) => {
|
||||
if (option) {
|
||||
setValue(`offers.${index}.offersFullTime.title`, option.value);
|
||||
}
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
<FormTextInput
|
||||
@ -90,10 +107,17 @@ function FullTimeOfferDetailsForm({
|
||||
<div>
|
||||
<CompaniesTypeahead
|
||||
required={true}
|
||||
// @ts-ignore TODO(offers): handle potentially null value.
|
||||
onSelect={({ value }) =>
|
||||
setValue(`offers.${index}.companyId`, value)
|
||||
}
|
||||
value={{
|
||||
id: watchCompanyId,
|
||||
label: watchCompanyName,
|
||||
value: watchCompanyId,
|
||||
}}
|
||||
onSelect={(option) => {
|
||||
if (option) {
|
||||
setValue(`offers.${index}.companyId`, option.value);
|
||||
setValue(`offers.${index}.companyName`, option.label);
|
||||
}
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
<FormSelect
|
||||
@ -270,19 +294,34 @@ function InternshipOfferDetailsForm({
|
||||
const { register, formState, setValue } = useFormContext<{
|
||||
offers: Array<OfferFormData>;
|
||||
}>();
|
||||
|
||||
const offerFields = formState.errors.offers?.[index];
|
||||
|
||||
const watchJobTitle = useWatch({
|
||||
name: `offers.${index}.offersIntern.title`,
|
||||
});
|
||||
const watchCompanyId = useWatch({
|
||||
name: `offers.${index}.companyId`,
|
||||
});
|
||||
const watchCompanyName = useWatch({
|
||||
name: `offers.${index}.companyName`,
|
||||
});
|
||||
|
||||
return (
|
||||
<div className="my-5 rounded-lg border border-slate-200 px-10 py-5">
|
||||
<div className="mb-5 grid grid-cols-2 space-x-3">
|
||||
<div>
|
||||
<JobTitlesTypeahead
|
||||
required={true}
|
||||
// @ts-ignore TODO(offers): handle potentially null value.
|
||||
onSelect={({ value }) =>
|
||||
setValue(`offers.${index}.offersIntern.title`, value)
|
||||
}
|
||||
value={{
|
||||
id: watchJobTitle,
|
||||
label: getLabelForJobTitleType(watchJobTitle as JobTitleType),
|
||||
value: watchJobTitle,
|
||||
}}
|
||||
onSelect={(option) => {
|
||||
if (option) {
|
||||
setValue(`offers.${index}.offersIntern.title`, option.value);
|
||||
}
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
@ -290,10 +329,17 @@ function InternshipOfferDetailsForm({
|
||||
<div>
|
||||
<CompaniesTypeahead
|
||||
required={true}
|
||||
// @ts-ignore TODO(offers): handle potentially null value.
|
||||
onSelect={({ value }) =>
|
||||
setValue(`offers.${index}.companyId`, value)
|
||||
}
|
||||
value={{
|
||||
id: watchCompanyId,
|
||||
label: watchCompanyName,
|
||||
value: watchCompanyId,
|
||||
}}
|
||||
onSelect={(option) => {
|
||||
if (option) {
|
||||
setValue(`offers.${index}.companyId`, option.value);
|
||||
setValue(`offers.${index}.companyName`, option.label);
|
||||
}
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
<FormSelect
|
||||
|
@ -7,6 +7,7 @@ import {
|
||||
import { HorizontalDivider } from '@tih/ui';
|
||||
|
||||
import type { OfferDisplayData } from '~/components/offers/types';
|
||||
import { JobTypeLabel } from '~/components/offers/types';
|
||||
|
||||
type Props = Readonly<{
|
||||
offer: OfferDisplayData;
|
||||
@ -20,6 +21,7 @@ export default function OfferCard({
|
||||
duration,
|
||||
jobTitle,
|
||||
jobLevel,
|
||||
jobType,
|
||||
location,
|
||||
receivedMonth,
|
||||
totalCompensation,
|
||||
@ -40,7 +42,10 @@ export default function OfferCard({
|
||||
</span>
|
||||
</div>
|
||||
<div className="ml-6 flex flex-row">
|
||||
<p>{jobLevel ? `${jobTitle}, ${jobLevel}` : jobTitle}</p>
|
||||
<p>
|
||||
{jobLevel ? `${jobTitle}, ${jobLevel}` : jobTitle}{' '}
|
||||
{jobType && `(${JobTypeLabel[jobType]})`}
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
{!duration && receivedMonth && (
|
||||
|
@ -10,6 +10,7 @@ import { Button, Dialog, Spinner, Tabs } from '@tih/ui';
|
||||
|
||||
import ProfilePhotoHolder from '~/components/offers/profile/ProfilePhotoHolder';
|
||||
import type { BackgroundDisplayData } from '~/components/offers/types';
|
||||
import { JobTypeLabel } from '~/components/offers/types';
|
||||
|
||||
import { getProfileEditPath } from '~/utils/offers/link';
|
||||
|
||||
@ -95,8 +96,8 @@ export default function ProfileHeader({
|
||||
title="Are you sure you want to delete this offer profile?"
|
||||
onClose={() => setIsDialogOpen(false)}>
|
||||
<div>
|
||||
All comments will be gone. You will not be able to access or
|
||||
recover it.
|
||||
All information and comments in this offer profile will be
|
||||
deleted. You will not be able to access or recover them.
|
||||
</div>
|
||||
</Dialog>
|
||||
)}
|
||||
@ -144,7 +145,11 @@ export default function ProfileHeader({
|
||||
<span>
|
||||
{`${experiences[0].companyName || ''} ${
|
||||
experiences[0].jobLevel || ''
|
||||
} ${experiences[0].jobTitle || ''}`}
|
||||
} ${experiences[0].jobTitle || ''} ${
|
||||
experiences[0].jobType
|
||||
? `(${JobTypeLabel[experiences[0].jobType]})`
|
||||
: ''
|
||||
}`}
|
||||
</span>
|
||||
</div>
|
||||
)}
|
||||
|
@ -45,6 +45,7 @@ export type BackgroundPostData = {
|
||||
|
||||
type ExperiencePostData = {
|
||||
companyId?: string | null;
|
||||
companyName?: string | null;
|
||||
durationInMonths?: number | null;
|
||||
id?: string;
|
||||
jobType?: string | null;
|
||||
@ -76,6 +77,7 @@ type SpecificYoe = SpecificYoePostData;
|
||||
export type OfferPostData = {
|
||||
comments: string;
|
||||
companyId: string;
|
||||
companyName?: string;
|
||||
id?: string;
|
||||
jobType: JobType;
|
||||
location: string;
|
||||
@ -129,6 +131,7 @@ export type OfferDisplayData = {
|
||||
id?: string;
|
||||
jobLevel?: string | null;
|
||||
jobTitle?: string | null;
|
||||
jobType?: JobType;
|
||||
location?: string | null;
|
||||
monthlySalary?: string | null;
|
||||
negotiationStrategy?: string | null;
|
||||
|
@ -24,9 +24,6 @@ import type { Profile, ProfileAnalysis, ProfileOffer } from '~/types/offers';
|
||||
|
||||
export default function OfferProfile() {
|
||||
const { showToast } = useToast();
|
||||
const ErrorPage = (
|
||||
<Error statusCode={404} title="Requested profile does not exist." />
|
||||
);
|
||||
const router = useRouter();
|
||||
const { offerProfileId, token = '' } = router.query;
|
||||
const [isEditable, setIsEditable] = useState(false);
|
||||
@ -126,6 +123,7 @@ export default function OfferProfile() {
|
||||
jobTitle: experience.title
|
||||
? getLabelForJobTitleType(experience.title as JobTitleType)
|
||||
: null,
|
||||
jobType: experience.jobType || undefined,
|
||||
monthlySalary: experience.monthlySalary
|
||||
? convertMoneyToString(experience.monthlySalary)
|
||||
: null,
|
||||
@ -177,7 +175,11 @@ export default function OfferProfile() {
|
||||
|
||||
return (
|
||||
<>
|
||||
{getProfileQuery.isError && ErrorPage}
|
||||
{getProfileQuery.isError && (
|
||||
<div className="flex w-full justify-center">
|
||||
<Error statusCode={404} title="Requested profile does not exist" />
|
||||
</div>
|
||||
)}
|
||||
{!getProfileQuery.isError && (
|
||||
<div className="mb-4 flex flex h-screen w-screen items-center justify-center divide-x">
|
||||
<div className="h-full w-2/3 divide-y">
|
||||
|
@ -1,3 +1,4 @@
|
||||
import Error from 'next/error';
|
||||
import { useRouter } from 'next/router';
|
||||
import { useState } from 'react';
|
||||
import { JobType } from '@prisma/client';
|
||||
@ -36,6 +37,7 @@ export default function OffersEditPage() {
|
||||
? [{ jobType: JobType.FULLTIME }]
|
||||
: experiences.map((exp) => ({
|
||||
companyId: exp.company?.id,
|
||||
companyName: exp.company?.name,
|
||||
durationInMonths: exp.durationInMonths,
|
||||
id: exp.id,
|
||||
jobType: exp.jobType,
|
||||
@ -53,6 +55,7 @@ export default function OffersEditPage() {
|
||||
offers: data.offers.map((offer) => ({
|
||||
comments: offer.comments,
|
||||
companyId: offer.company.id,
|
||||
companyName: offer.company.name,
|
||||
id: offer.id,
|
||||
jobType: offer.jobType,
|
||||
location: offer.location,
|
||||
@ -74,6 +77,11 @@ export default function OffersEditPage() {
|
||||
|
||||
return (
|
||||
<>
|
||||
{getProfileResult.isError && (
|
||||
<div className="flex w-full justify-center">
|
||||
<Error statusCode={404} title="Requested profile does not exist" />
|
||||
</div>
|
||||
)}
|
||||
{getProfileResult.isLoading && (
|
||||
<div className="flex w-full justify-center">
|
||||
<Spinner className="m-10" display="block" size="lg" />
|
||||
|
Reference in New Issue
Block a user