[offers][fix] Fix offers UI (#460)

pull/463/head
Ai Ling 2 years ago committed by GitHub
parent 42e990f180
commit a8fdca65cd
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23

@ -19,12 +19,14 @@ type OfferAnalysisData = {
type OfferAnalysisContentProps = Readonly<{ type OfferAnalysisContentProps = Readonly<{
analysis: OfferAnalysisData; analysis: OfferAnalysisData;
isSubmission: boolean;
tab: string; tab: string;
}>; }>;
function OfferAnalysisContent({ function OfferAnalysisContent({
analysis: { offer, offerAnalysis }, analysis: { offer, offerAnalysis },
tab, tab,
isSubmission,
}: OfferAnalysisContentProps) { }: OfferAnalysisContentProps) {
if (!offerAnalysis || !offer || offerAnalysis.noOfOffers === 0) { if (!offerAnalysis || !offer || offerAnalysis.noOfOffers === 0) {
if (tab === OVERALL_TAB) { if (tab === OVERALL_TAB) {
@ -46,16 +48,30 @@ function OfferAnalysisContent({
<> <>
<OfferPercentileAnalysisText <OfferPercentileAnalysisText
companyName={offer.company.name} companyName={offer.company.name}
isSubmission={isSubmission}
offerAnalysis={offerAnalysis} offerAnalysis={offerAnalysis}
tab={tab} 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) => ( {offerAnalysis.topPercentileOffers.map((topPercentileOffer) => (
<OfferProfileCard <OfferProfileCard
key={topPercentileOffer.id} key={topPercentileOffer.id}
offerProfile={topPercentileOffer} 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; allAnalysis?: ProfileAnalysis | null;
isError: boolean; isError: boolean;
isLoading: boolean; isLoading: boolean;
isSubmission?: boolean;
}>; }>;
export default function OfferAnalysis({ export default function OfferAnalysis({
allAnalysis, allAnalysis,
isError, isError,
isLoading, isLoading,
isSubmission = false,
}: OfferAnalysisProps) { }: OfferAnalysisProps) {
const [tab, setTab] = useState(OVERALL_TAB); const [tab, setTab] = useState(OVERALL_TAB);
const [analysis, setAnalysis] = useState<OfferAnalysisData | null>(null); const [analysis, setAnalysis] = useState<OfferAnalysisData | null>(null);
@ -117,7 +135,11 @@ export default function OfferAnalysis({
onChange={setTab} onChange={setTab}
/> />
<HorizontalDivider className="mb-5" /> <HorizontalDivider className="mb-5" />
<OfferAnalysisContent analysis={analysis} tab={tab} /> <OfferAnalysisContent
analysis={analysis}
isSubmission={isSubmission}
tab={tab}
/>
</div> </div>
)} )}
</div> </div>

@ -4,6 +4,7 @@ import type { Analysis } from '~/types/offers';
type OfferPercentileAnalysisTextProps = Readonly<{ type OfferPercentileAnalysisTextProps = Readonly<{
companyName: string; companyName: string;
isSubmission: boolean;
offerAnalysis: Analysis; offerAnalysis: Analysis;
tab: string; tab: string;
}>; }>;
@ -12,18 +13,21 @@ export default function OfferPercentileAnalysisText({
tab, tab,
companyName, companyName,
offerAnalysis: { noOfOffers, percentile }, offerAnalysis: { noOfOffers, percentile },
isSubmission,
}: OfferPercentileAnalysisTextProps) { }: OfferPercentileAnalysisTextProps) {
return tab === OVERALL_TAB ? ( return tab === OVERALL_TAB ? (
<p> <p>
Your highest offer is from <b>{companyName}</b>, which is{' '} {isSubmission ? 'Your' : "This profile's"} highest offer is from{' '}
<b>{percentile.toFixed(1)}</b> percentile out of <b>{noOfOffers}</b>{' '} <b>{companyName}</b>, which is <b>{percentile.toFixed(1)}</b> percentile
offers received for the same job title and YOE(±1) in the last year. out of <b>{noOfOffers}</b> offers received for the same job title and
YOE(±1) in the last year.
</p> </p>
) : ( ) : (
<p> <p>
Your offer from <b>{companyName}</b> is <b>{percentile.toFixed(1)}</b>{' '} {isSubmission ? 'Your' : 'The'} offer from <b>{companyName}</b> is{' '}
percentile out of <b>{noOfOffers}</b> offers received in {companyName} for <b>{percentile.toFixed(1)}</b> percentile out of <b>{noOfOffers}</b>{' '}
the same job title and YOE(±1) in the last year. offers received in {companyName} for the same job title and YOE(±1) in the
last year.
</p> </p>
); );
} }

@ -12,6 +12,7 @@ import { convertMoneyToString } from '~/utils/offers/currency';
import { formatDate } from '~/utils/offers/time'; import { formatDate } from '~/utils/offers/time';
import ProfilePhotoHolder from '../profile/ProfilePhotoHolder'; import ProfilePhotoHolder from '../profile/ProfilePhotoHolder';
import { JobTypeLabel } from '../types';
import type { AnalysisOffer } from '~/types/offers'; import type { AnalysisOffer } from '~/types/offers';
@ -34,7 +35,12 @@ export default function OfferProfileCard({
}, },
}: OfferProfileCardProps) { }: OfferProfileCardProps) {
return ( 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 className="flex items-center gap-x-5">
<div> <div>
<ProfilePhotoHolder size="sm" /> <ProfilePhotoHolder size="sm" />
@ -58,7 +64,8 @@ export default function OfferProfileCard({
<div className="flex items-end justify-between"> <div className="flex items-end justify-between">
<div className="col-span-1 row-span-3"> <div className="col-span-1 row-span-3">
<p className="font-bold"> <p className="font-bold">
{getLabelForJobTitleType(title as JobTitleType)} {getLabelForJobTitleType(title as JobTitleType)}{' '}
{`(${JobTypeLabel[jobType]})`}
</p> </p>
<p> <p>
Company: {company.name}, {location} Company: {company.name}, {location}

@ -22,6 +22,7 @@ export default function OffersSubmissionAnalysis({
allAnalysis={analysis} allAnalysis={analysis}
isError={isError} isError={isError}
isLoading={isLoading} isLoading={isLoading}
isSubmission={true}
/> />
</div> </div>
); );

@ -27,6 +27,7 @@ import { trpc } from '~/utils/trpc';
const defaultOfferValues = { const defaultOfferValues = {
comments: '', comments: '',
companyId: '', companyId: '',
jobTitle: '',
jobType: JobType.FULLTIME, jobType: JobType.FULLTIME,
location: '', location: '',
monthYearReceived: { monthYearReceived: {
@ -39,11 +40,38 @@ const defaultOfferValues = {
export const defaultFullTimeOfferValues = { export const defaultFullTimeOfferValues = {
...defaultOfferValues, ...defaultOfferValues,
jobType: JobType.FULLTIME, 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 = { export const defaultInternshipOfferValues = {
...defaultOfferValues, ...defaultOfferValues,
jobType: JobType.INTERN, jobType: JobType.INTERN,
offersIntern: {
internshipCycle: null,
monthlySalary: {
currency: 'SGD',
value: null,
},
startYear: null,
},
}; };
const defaultOfferProfileValues = { const defaultOfferProfileValues = {
@ -198,6 +226,32 @@ export default function OffersSubmissionForm({
scrollToTop(); scrollToTop();
}, [step]); }, [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 ( return (
<div ref={pageRef} className="fixed h-full w-full overflow-y-scroll"> <div ref={pageRef} className="fixed h-full w-full overflow-y-scroll">
<div className="mb-20 flex justify-center"> <div className="mb-20 flex justify-center">
@ -210,7 +264,7 @@ export default function OffersSubmissionForm({
/> />
</div> </div>
<FormProvider {...formMethods}> <FormProvider {...formMethods}>
<form onSubmit={handleSubmit(onSubmit)}> <form className="text-sm" onSubmit={handleSubmit(onSubmit)}>
{steps[step]} {steps[step]}
{/* <pre>{JSON.stringify(formMethods.watch(), null, 2)}</pre> */} {/* <pre>{JSON.stringify(formMethods.watch(), null, 2)}</pre> */}
{step === 0 && ( {step === 0 && (

@ -11,6 +11,8 @@ import {
} from '~/components/offers/constants'; } from '~/components/offers/constants';
import type { BackgroundPostData } from '~/components/offers/types'; import type { BackgroundPostData } from '~/components/offers/types';
import CompaniesTypeahead from '~/components/shared/CompaniesTypeahead'; 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 JobTitlesTypeahead from '~/components/shared/JobTitlesTypahead';
import { import {
@ -92,23 +94,47 @@ function FullTimeJobFields() {
background: BackgroundPostData; background: BackgroundPostData;
}>(); }>();
const experiencesField = formState.errors.background?.experiences?.[0]; 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 ( return (
<> <>
<div className="mb-5 grid grid-cols-2 space-x-3"> <div className="mb-5 grid grid-cols-2 space-x-3">
<div> <div>
<JobTitlesTypeahead <JobTitlesTypeahead
// @ts-ignore TODO(offers): handle potentially null value. value={{
onSelect={({ value }) => id: watchJobTitle,
setValue(`background.experiences.0.title`, value) label: getLabelForJobTitleType(watchJobTitle as JobTitleType),
value: watchJobTitle,
}}
onSelect={(option) => {
if (option) {
setValue('background.experiences.0.title', option.value);
} }
}}
/> />
</div> </div>
<div> <div>
<CompaniesTypeahead <CompaniesTypeahead
// @ts-ignore TODO(offers): handle potentially null value. value={{
onSelect={({ value }) => id: watchCompanyId,
setValue(`background.experiences.0.companyId`, value) label: watchCompanyName,
value: watchCompanyId,
}}
onSelect={(option) => {
if (option) {
setValue('background.experiences.0.companyId', option.value);
setValue('background.experiences.0.companyName', option.label);
} }
}}
/> />
</div> </div>
</div> </div>
@ -175,23 +201,46 @@ function InternshipJobFields() {
}>(); }>();
const experiencesField = formState.errors.background?.experiences?.[0]; 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 ( return (
<> <>
<div className="mb-5 grid grid-cols-2 space-x-3"> <div className="mb-5 grid grid-cols-2 space-x-3">
<div> <div>
<JobTitlesTypeahead <JobTitlesTypeahead
// @ts-ignore TODO(offers): handle potentially null value. value={{
onSelect={({ value }) => id: watchJobTitle,
setValue(`background.experiences.0.title`, value) label: getLabelForJobTitleType(watchJobTitle as JobTitleType),
value: watchJobTitle,
}}
onSelect={(option) => {
if (option) {
setValue('background.experiences.0.title', option.value);
} }
}}
/> />
</div> </div>
<div> <div>
<CompaniesTypeahead <CompaniesTypeahead
// @ts-ignore TODO(offers): handle potentially null value. value={{
onSelect={({ value }) => id: watchCompanyId,
setValue(`background.experiences.0.companyId`, value) label: watchCompanyName,
value: watchCompanyId,
}}
onSelect={(option) => {
if (option) {
setValue('background.experiences.0.companyId', option.value);
setValue('background.experiences.0.companyName', option.label);
} }
}}
/> />
</div> </div>
</div> </div>

@ -13,6 +13,8 @@ import { JobType } from '@prisma/client';
import { Button, Dialog } from '@tih/ui'; import { Button, Dialog } from '@tih/ui';
import CompaniesTypeahead from '~/components/shared/CompaniesTypeahead'; 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 JobTitlesTypeahead from '~/components/shared/JobTitlesTypahead';
import { import {
@ -51,6 +53,15 @@ function FullTimeOfferDetailsForm({
}>(); }>();
const offerFields = formState.errors.offers?.[index]; 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({ const watchCurrency = useWatch({
name: `offers.${index}.offersFullTime.totalCompensation.currency`, name: `offers.${index}.offersFullTime.totalCompensation.currency`,
}); });
@ -70,10 +81,16 @@ function FullTimeOfferDetailsForm({
<div> <div>
<JobTitlesTypeahead <JobTitlesTypeahead
required={true} required={true}
// @ts-ignore TODO(offers): handle potentially null value. value={{
onSelect={({ value }) => id: watchJobTitle,
setValue(`offers.${index}.offersFullTime.title`, value) label: getLabelForJobTitleType(watchJobTitle as JobTitleType),
value: watchJobTitle,
}}
onSelect={(option) => {
if (option) {
setValue(`offers.${index}.offersFullTime.title`, option.value);
} }
}}
/> />
</div> </div>
<FormTextInput <FormTextInput
@ -90,10 +107,17 @@ function FullTimeOfferDetailsForm({
<div> <div>
<CompaniesTypeahead <CompaniesTypeahead
required={true} required={true}
// @ts-ignore TODO(offers): handle potentially null value. value={{
onSelect={({ value }) => id: watchCompanyId,
setValue(`offers.${index}.companyId`, value) label: watchCompanyName,
value: watchCompanyId,
}}
onSelect={(option) => {
if (option) {
setValue(`offers.${index}.companyId`, option.value);
setValue(`offers.${index}.companyName`, option.label);
} }
}}
/> />
</div> </div>
<FormSelect <FormSelect
@ -270,19 +294,34 @@ function InternshipOfferDetailsForm({
const { register, formState, setValue } = useFormContext<{ const { register, formState, setValue } = useFormContext<{
offers: Array<OfferFormData>; offers: Array<OfferFormData>;
}>(); }>();
const offerFields = formState.errors.offers?.[index]; 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 ( return (
<div className="my-5 rounded-lg border border-slate-200 px-10 py-5"> <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 className="mb-5 grid grid-cols-2 space-x-3">
<div> <div>
<JobTitlesTypeahead <JobTitlesTypeahead
required={true} required={true}
// @ts-ignore TODO(offers): handle potentially null value. value={{
onSelect={({ value }) => id: watchJobTitle,
setValue(`offers.${index}.offersIntern.title`, value) label: getLabelForJobTitleType(watchJobTitle as JobTitleType),
value: watchJobTitle,
}}
onSelect={(option) => {
if (option) {
setValue(`offers.${index}.offersIntern.title`, option.value);
} }
}}
/> />
</div> </div>
</div> </div>
@ -290,10 +329,17 @@ function InternshipOfferDetailsForm({
<div> <div>
<CompaniesTypeahead <CompaniesTypeahead
required={true} required={true}
// @ts-ignore TODO(offers): handle potentially null value. value={{
onSelect={({ value }) => id: watchCompanyId,
setValue(`offers.${index}.companyId`, value) label: watchCompanyName,
value: watchCompanyId,
}}
onSelect={(option) => {
if (option) {
setValue(`offers.${index}.companyId`, option.value);
setValue(`offers.${index}.companyName`, option.label);
} }
}}
/> />
</div> </div>
<FormSelect <FormSelect

@ -7,6 +7,7 @@ import {
import { HorizontalDivider } from '@tih/ui'; import { HorizontalDivider } from '@tih/ui';
import type { OfferDisplayData } from '~/components/offers/types'; import type { OfferDisplayData } from '~/components/offers/types';
import { JobTypeLabel } from '~/components/offers/types';
type Props = Readonly<{ type Props = Readonly<{
offer: OfferDisplayData; offer: OfferDisplayData;
@ -20,6 +21,7 @@ export default function OfferCard({
duration, duration,
jobTitle, jobTitle,
jobLevel, jobLevel,
jobType,
location, location,
receivedMonth, receivedMonth,
totalCompensation, totalCompensation,
@ -40,7 +42,10 @@ export default function OfferCard({
</span> </span>
</div> </div>
<div className="ml-6 flex flex-row"> <div className="ml-6 flex flex-row">
<p>{jobLevel ? `${jobTitle}, ${jobLevel}` : jobTitle}</p> <p>
{jobLevel ? `${jobTitle}, ${jobLevel}` : jobTitle}{' '}
{jobType && `(${JobTypeLabel[jobType]})`}
</p>
</div> </div>
</div> </div>
{!duration && receivedMonth && ( {!duration && receivedMonth && (

@ -10,6 +10,7 @@ import { Button, Dialog, Spinner, Tabs } from '@tih/ui';
import ProfilePhotoHolder from '~/components/offers/profile/ProfilePhotoHolder'; import ProfilePhotoHolder from '~/components/offers/profile/ProfilePhotoHolder';
import type { BackgroundDisplayData } from '~/components/offers/types'; import type { BackgroundDisplayData } from '~/components/offers/types';
import { JobTypeLabel } from '~/components/offers/types';
import { getProfileEditPath } from '~/utils/offers/link'; 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?" title="Are you sure you want to delete this offer profile?"
onClose={() => setIsDialogOpen(false)}> onClose={() => setIsDialogOpen(false)}>
<div> <div>
All comments will be gone. You will not be able to access or All information and comments in this offer profile will be
recover it. deleted. You will not be able to access or recover them.
</div> </div>
</Dialog> </Dialog>
)} )}
@ -144,7 +145,11 @@ export default function ProfileHeader({
<span> <span>
{`${experiences[0].companyName || ''} ${ {`${experiences[0].companyName || ''} ${
experiences[0].jobLevel || '' experiences[0].jobLevel || ''
} ${experiences[0].jobTitle || ''}`} } ${experiences[0].jobTitle || ''} ${
experiences[0].jobType
? `(${JobTypeLabel[experiences[0].jobType]})`
: ''
}`}
</span> </span>
</div> </div>
)} )}

@ -45,6 +45,7 @@ export type BackgroundPostData = {
type ExperiencePostData = { type ExperiencePostData = {
companyId?: string | null; companyId?: string | null;
companyName?: string | null;
durationInMonths?: number | null; durationInMonths?: number | null;
id?: string; id?: string;
jobType?: string | null; jobType?: string | null;
@ -76,6 +77,7 @@ type SpecificYoe = SpecificYoePostData;
export type OfferPostData = { export type OfferPostData = {
comments: string; comments: string;
companyId: string; companyId: string;
companyName?: string;
id?: string; id?: string;
jobType: JobType; jobType: JobType;
location: string; location: string;
@ -129,6 +131,7 @@ export type OfferDisplayData = {
id?: string; id?: string;
jobLevel?: string | null; jobLevel?: string | null;
jobTitle?: string | null; jobTitle?: string | null;
jobType?: JobType;
location?: string | null; location?: string | null;
monthlySalary?: string | null; monthlySalary?: string | null;
negotiationStrategy?: string | null; negotiationStrategy?: string | null;

@ -24,9 +24,6 @@ import type { Profile, ProfileAnalysis, ProfileOffer } from '~/types/offers';
export default function OfferProfile() { export default function OfferProfile() {
const { showToast } = useToast(); const { showToast } = useToast();
const ErrorPage = (
<Error statusCode={404} title="Requested profile does not exist." />
);
const router = useRouter(); const router = useRouter();
const { offerProfileId, token = '' } = router.query; const { offerProfileId, token = '' } = router.query;
const [isEditable, setIsEditable] = useState(false); const [isEditable, setIsEditable] = useState(false);
@ -126,6 +123,7 @@ export default function OfferProfile() {
jobTitle: experience.title jobTitle: experience.title
? getLabelForJobTitleType(experience.title as JobTitleType) ? getLabelForJobTitleType(experience.title as JobTitleType)
: null, : null,
jobType: experience.jobType || undefined,
monthlySalary: experience.monthlySalary monthlySalary: experience.monthlySalary
? convertMoneyToString(experience.monthlySalary) ? convertMoneyToString(experience.monthlySalary)
: null, : null,
@ -177,7 +175,11 @@ export default function OfferProfile() {
return ( 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 && ( {!getProfileQuery.isError && (
<div className="mb-4 flex flex h-screen w-screen items-center justify-center divide-x"> <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"> <div className="h-full w-2/3 divide-y">

@ -1,3 +1,4 @@
import Error from 'next/error';
import { useRouter } from 'next/router'; import { useRouter } from 'next/router';
import { useState } from 'react'; import { useState } from 'react';
import { JobType } from '@prisma/client'; import { JobType } from '@prisma/client';
@ -36,6 +37,7 @@ export default function OffersEditPage() {
? [{ jobType: JobType.FULLTIME }] ? [{ jobType: JobType.FULLTIME }]
: experiences.map((exp) => ({ : experiences.map((exp) => ({
companyId: exp.company?.id, companyId: exp.company?.id,
companyName: exp.company?.name,
durationInMonths: exp.durationInMonths, durationInMonths: exp.durationInMonths,
id: exp.id, id: exp.id,
jobType: exp.jobType, jobType: exp.jobType,
@ -53,6 +55,7 @@ export default function OffersEditPage() {
offers: data.offers.map((offer) => ({ offers: data.offers.map((offer) => ({
comments: offer.comments, comments: offer.comments,
companyId: offer.company.id, companyId: offer.company.id,
companyName: offer.company.name,
id: offer.id, id: offer.id,
jobType: offer.jobType, jobType: offer.jobType,
location: offer.location, location: offer.location,
@ -74,6 +77,11 @@ export default function OffersEditPage() {
return ( return (
<> <>
{getProfileResult.isError && (
<div className="flex w-full justify-center">
<Error statusCode={404} title="Requested profile does not exist" />
</div>
)}
{getProfileResult.isLoading && ( {getProfileResult.isLoading && (
<div className="flex w-full justify-center"> <div className="flex w-full justify-center">
<Spinner className="m-10" display="block" size="lg" /> <Spinner className="m-10" display="block" size="lg" />

Loading…
Cancel
Save