mirror of
https://github.com/yangshun/tech-interview-handbook.git
synced 2026-05-04 00:11:10 +08:00
[offers][feat] Tweak UI and add View More button to OEA (#507)
* [offers][feat] Tweak UI and minor refactoring * [offers][feat] Add view more button to OEA
This commit is contained in:
@@ -63,7 +63,7 @@ export default function DashboardProfileCard({
|
|||||||
{profileName}
|
{profileName}
|
||||||
</h2>
|
</h2>
|
||||||
<p className="text-sm text-slate-500">
|
<p className="text-sm text-slate-500">
|
||||||
<span>Created at {formatDate(createdAt)}</span>
|
<span>Created in {formatDate(createdAt)}</span>
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -1,10 +1,13 @@
|
|||||||
import { useEffect } from 'react';
|
import { useEffect } from 'react';
|
||||||
import { useState } from 'react';
|
import { useState } from 'react';
|
||||||
import { Alert, HorizontalDivider, Spinner, Tabs } from '@tih/ui';
|
import { ArrowUpRightIcon } from '@heroicons/react/24/outline';
|
||||||
|
import { JobType } from '@prisma/client';
|
||||||
|
import { Alert, Button, HorizontalDivider, Spinner, Tabs } from '@tih/ui';
|
||||||
|
|
||||||
import OfferPercentileAnalysisText from './OfferPercentileAnalysisText';
|
import OfferPercentileAnalysisText from './OfferPercentileAnalysisText';
|
||||||
import OfferProfileCard from './OfferProfileCard';
|
import OfferProfileCard from './OfferProfileCard';
|
||||||
import { OVERALL_TAB } from '../constants';
|
import { OVERALL_TAB } from '../constants';
|
||||||
|
import { YOE_CATEGORY } from '../table/types';
|
||||||
|
|
||||||
import type { AnalysisUnit, ProfileAnalysis } from '~/types/offers';
|
import type { AnalysisUnit, ProfileAnalysis } from '~/types/offers';
|
||||||
|
|
||||||
@@ -19,6 +22,16 @@ function OfferAnalysisContent({
|
|||||||
tab,
|
tab,
|
||||||
isSubmission,
|
isSubmission,
|
||||||
}: OfferAnalysisContentProps) {
|
}: OfferAnalysisContentProps) {
|
||||||
|
const { companyId, companyName, title, totalYoe, jobType } = analysis;
|
||||||
|
const yoeCategory =
|
||||||
|
jobType === JobType.INTERN
|
||||||
|
? ''
|
||||||
|
: totalYoe <= 2
|
||||||
|
? YOE_CATEGORY.ENTRY
|
||||||
|
: totalYoe <= 5
|
||||||
|
? YOE_CATEGORY.MID
|
||||||
|
: YOE_CATEGORY.SENIOR;
|
||||||
|
|
||||||
if (!analysis || analysis.noOfOffers === 0) {
|
if (!analysis || analysis.noOfOffers === 0) {
|
||||||
if (tab === OVERALL_TAB) {
|
if (tab === OVERALL_TAB) {
|
||||||
return (
|
return (
|
||||||
@@ -55,15 +68,22 @@ function OfferAnalysisContent({
|
|||||||
offerProfile={topPercentileOffer}
|
offerProfile={topPercentileOffer}
|
||||||
/>
|
/>
|
||||||
))}
|
))}
|
||||||
{/* {offerAnalysis.topPercentileOffers.length > 0 && (
|
{analysis.topPercentileOffers.length > 0 && (
|
||||||
<div className="mb-4 flex justify-end">
|
<div className="mb-4 flex justify-end">
|
||||||
<Button
|
<Button
|
||||||
icon={EllipsisHorizontalIcon}
|
href={
|
||||||
|
tab === OVERALL_TAB
|
||||||
|
? `/offers?jobTitleId=${title}&sortBy=-totalCompensation&yoeCategory=${yoeCategory}`
|
||||||
|
: `/offers?companyId=${companyId}&companyName=${companyName}&jobTitleId=${title}&sortBy=-totalCompensation&yoeCategory=${yoeCategory}`
|
||||||
|
}
|
||||||
|
icon={ArrowUpRightIcon}
|
||||||
label="View more offers"
|
label="View more offers"
|
||||||
|
rel="noreferrer"
|
||||||
|
target="_blank"
|
||||||
variant="tertiary"
|
variant="tertiary"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
)} */}
|
)}
|
||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@@ -133,4 +153,4 @@ export default function OfferAnalysis({
|
|||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,4 +1,10 @@
|
|||||||
import {
|
import {
|
||||||
|
ArrowTrendingUpIcon,
|
||||||
|
BuildingOfficeIcon,
|
||||||
|
MapPinIcon,
|
||||||
|
} from '@heroicons/react/20/solid';
|
||||||
|
import {
|
||||||
|
ArrowTopRightOnSquareIcon,
|
||||||
BuildingOffice2Icon,
|
BuildingOffice2Icon,
|
||||||
CalendarDaysIcon,
|
CalendarDaysIcon,
|
||||||
} from '@heroicons/react/24/outline';
|
} from '@heroicons/react/24/outline';
|
||||||
@@ -7,9 +13,8 @@ import { JobType } from '@prisma/client';
|
|||||||
import type { JobTitleType } from '~/components/shared/JobTitles';
|
import type { JobTitleType } from '~/components/shared/JobTitles';
|
||||||
import { getLabelForJobTitleType } from '~/components/shared/JobTitles';
|
import { getLabelForJobTitleType } from '~/components/shared/JobTitles';
|
||||||
|
|
||||||
import { HorizontalDivider } from '~/../../../packages/ui/dist';
|
import { Button } from '~/../../../packages/ui/dist';
|
||||||
import { convertMoneyToString } from '~/utils/offers/currency';
|
import { convertMoneyToString } from '~/utils/offers/currency';
|
||||||
import { getCompanyDisplayText } from '~/utils/offers/string';
|
|
||||||
import { formatDate } from '~/utils/offers/time';
|
import { formatDate } from '~/utils/offers/time';
|
||||||
|
|
||||||
import { JobTypeLabel } from '../constants';
|
import { JobTypeLabel } from '../constants';
|
||||||
@@ -36,52 +41,109 @@ export default function OfferProfileCard({
|
|||||||
profileId,
|
profileId,
|
||||||
},
|
},
|
||||||
}: OfferProfileCardProps) {
|
}: OfferProfileCardProps) {
|
||||||
return (
|
function UpperSection() {
|
||||||
<a
|
return (
|
||||||
className="my-5 block rounded-lg border bg-white p-4 px-8 shadow-md"
|
<div className="border-b px-4 py-5 sm:px-6">
|
||||||
href={`/offers/profile/${profileId}`}
|
<div className="-ml-4 -mt-4 flex flex-wrap items-center justify-between sm:flex-nowrap">
|
||||||
rel="noreferrer"
|
<div className="ml-4 mt-4">
|
||||||
target="_blank">
|
<div className="flex items-center">
|
||||||
<div className="flex items-center gap-x-5">
|
<div className="flex-shrink-0">
|
||||||
<div>
|
<ProfilePhotoHolder size="sm" />
|
||||||
<ProfilePhotoHolder size="sm" />
|
</div>
|
||||||
</div>
|
<div className="ml-4">
|
||||||
<div className="col-span-10">
|
<h2 className="text-lg font-medium leading-6 text-slate-900">
|
||||||
<p className="font-bold">{profileName}</p>
|
{profileName}
|
||||||
{previousCompanies.length > 0 && (
|
</h2>
|
||||||
<div className="flex flex-row">
|
<p className="flex text-sm text-slate-500">
|
||||||
<BuildingOffice2Icon className="mr-2 h-5" />
|
<CalendarDaysIcon className="mr-2 h-5" />
|
||||||
<span className="mr-2 font-bold">Current:</span>
|
<span className="mr-2 font-bold">YOE:</span>
|
||||||
<span>{previousCompanies[0]}</span>
|
<span>{totalYoe}</span>
|
||||||
|
{previousCompanies.length > 0 && (
|
||||||
|
<>
|
||||||
|
<BuildingOffice2Icon className="ml-4 mr-2 h-5" />
|
||||||
|
<span className="mr-2 font-bold">Previous:</span>
|
||||||
|
<span>{previousCompanies[0]}</span>
|
||||||
|
</>
|
||||||
|
)}
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
)}
|
</div>
|
||||||
<div className="flex flex-row">
|
<div className="ml-4 mt-4 flex flex-shrink-0">
|
||||||
<CalendarDaysIcon className="mr-2 h-5" />
|
<Button
|
||||||
<span className="mr-2 font-bold">YOE:</span>
|
href={`/offers/profile/${profileId}`}
|
||||||
<span>{totalYoe}</span>
|
icon={ArrowTopRightOnSquareIcon}
|
||||||
|
isLabelHidden={true}
|
||||||
|
label="View Profile"
|
||||||
|
rel="noreferrer"
|
||||||
|
size="md"
|
||||||
|
target="_blank"
|
||||||
|
variant="tertiary"
|
||||||
|
/>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
<HorizontalDivider />
|
function BottomSection() {
|
||||||
<div className="flex items-end justify-between">
|
return (
|
||||||
<div className="col-span-1 row-span-3">
|
<div className="px-4 py-4 sm:px-6">
|
||||||
<p className="font-bold">
|
<div className="flex items-end justify-between">
|
||||||
{getLabelForJobTitleType(title as JobTitleType)}{' '}
|
<div className="col-span-1 row-span-3">
|
||||||
{`(${JobTypeLabel[jobType]})`}
|
<h4 className="font-medium">
|
||||||
</p>
|
{getLabelForJobTitleType(title as JobTitleType)}{' '}
|
||||||
<p>{`Company: ${getCompanyDisplayText(company.name, location)}`}</p>
|
{jobType && <>({JobTypeLabel[jobType]})</>}
|
||||||
{level && <p>Level: {level}</p>}
|
</h4>
|
||||||
</div>
|
<div className="mt-1 flex flex-col sm:mt-0 sm:flex-row sm:flex-wrap sm:space-x-4">
|
||||||
<div className="col-span-1 row-span-3">
|
{company?.name && (
|
||||||
<p className="text-end">{formatDate(monthYearReceived)}</p>
|
<div className="mt-2 flex items-center text-sm text-slate-500">
|
||||||
<p className="text-end text-xl">
|
<BuildingOfficeIcon
|
||||||
{jobType === JobType.FULLTIME
|
aria-hidden="true"
|
||||||
? `${convertMoneyToString(income)} / year`
|
className="mr-1.5 h-5 w-5 flex-shrink-0 text-slate-400"
|
||||||
: `${convertMoneyToString(income)} / month`}
|
/>
|
||||||
</p>
|
{company.name}
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
{location && (
|
||||||
|
<div className="mt-2 flex items-center text-sm text-slate-500">
|
||||||
|
<MapPinIcon
|
||||||
|
aria-hidden="true"
|
||||||
|
className="mr-1.5 h-5 w-5 flex-shrink-0 text-slate-400"
|
||||||
|
/>
|
||||||
|
{location.cityName}
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
{level && (
|
||||||
|
<div className="mt-2 flex items-center text-sm text-slate-500">
|
||||||
|
<ArrowTrendingUpIcon
|
||||||
|
aria-hidden="true"
|
||||||
|
className="mr-1.5 h-5 w-5 flex-shrink-0 text-slate-400"
|
||||||
|
/>
|
||||||
|
{level}
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div className="col-span-1 row-span-3">
|
||||||
|
<p className="text-end text-lg font-medium leading-6 text-slate-900">
|
||||||
|
{jobType === JobType.FULLTIME
|
||||||
|
? `${convertMoneyToString(income)} / year`
|
||||||
|
: `${convertMoneyToString(income)} / month`}
|
||||||
|
</p>
|
||||||
|
<p className="text-end text-sm text-slate-500">
|
||||||
|
{formatDate(monthYearReceived)}
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</a>
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className="my-5 block rounded-lg border border-slate-200 bg-white">
|
||||||
|
<UpperSection />
|
||||||
|
<BottomSection />
|
||||||
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -13,10 +13,12 @@ import BackgroundForm from '~/components/offers/offersSubmission/submissionForm/
|
|||||||
import OfferDetailsForm from '~/components/offers/offersSubmission/submissionForm/OfferDetailsForm';
|
import OfferDetailsForm from '~/components/offers/offersSubmission/submissionForm/OfferDetailsForm';
|
||||||
import type {
|
import type {
|
||||||
OfferFormData,
|
OfferFormData,
|
||||||
|
OfferPostData,
|
||||||
OffersProfileFormData,
|
OffersProfileFormData,
|
||||||
} from '~/components/offers/types';
|
} from '~/components/offers/types';
|
||||||
import type { Month } from '~/components/shared/MonthYearPicker';
|
import type { Month } from '~/components/shared/MonthYearPicker';
|
||||||
|
|
||||||
|
import { Currency } from '~/utils/offers/currency/CurrencyEnum';
|
||||||
import {
|
import {
|
||||||
cleanObject,
|
cleanObject,
|
||||||
removeEmptyObjects,
|
removeEmptyObjects,
|
||||||
@@ -25,6 +27,8 @@ import {
|
|||||||
import { getCurrentMonth, getCurrentYear } from '~/utils/offers/time';
|
import { getCurrentMonth, getCurrentYear } from '~/utils/offers/time';
|
||||||
import { trpc } from '~/utils/trpc';
|
import { trpc } from '~/utils/trpc';
|
||||||
|
|
||||||
|
export const DEFAULT_CURRENCY = Currency.SGD;
|
||||||
|
|
||||||
const defaultOfferValues = {
|
const defaultOfferValues = {
|
||||||
cityId: '',
|
cityId: '',
|
||||||
comments: '',
|
comments: '',
|
||||||
@@ -43,21 +47,17 @@ export const defaultFullTimeOfferValues = {
|
|||||||
jobType: JobType.FULLTIME,
|
jobType: JobType.FULLTIME,
|
||||||
offersFullTime: {
|
offersFullTime: {
|
||||||
baseSalary: {
|
baseSalary: {
|
||||||
currency: 'SGD',
|
currency: DEFAULT_CURRENCY,
|
||||||
value: null,
|
|
||||||
},
|
},
|
||||||
bonus: {
|
bonus: {
|
||||||
currency: 'SGD',
|
currency: DEFAULT_CURRENCY,
|
||||||
value: null,
|
|
||||||
},
|
},
|
||||||
level: '',
|
level: '',
|
||||||
stocks: {
|
stocks: {
|
||||||
currency: 'SGD',
|
currency: DEFAULT_CURRENCY,
|
||||||
value: null,
|
|
||||||
},
|
},
|
||||||
totalCompensation: {
|
totalCompensation: {
|
||||||
currency: 'SGD',
|
currency: DEFAULT_CURRENCY,
|
||||||
value: null,
|
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
@@ -66,16 +66,15 @@ export const defaultInternshipOfferValues = {
|
|||||||
...defaultOfferValues,
|
...defaultOfferValues,
|
||||||
jobType: JobType.INTERN,
|
jobType: JobType.INTERN,
|
||||||
offersIntern: {
|
offersIntern: {
|
||||||
internshipCycle: null,
|
internshipCycle: '',
|
||||||
monthlySalary: {
|
monthlySalary: {
|
||||||
currency: 'SGD',
|
currency: DEFAULT_CURRENCY,
|
||||||
value: null,
|
|
||||||
},
|
},
|
||||||
startYear: null,
|
startYear: null,
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
const defaultOfferProfileValues = {
|
const defaultOfferProfileValues: OffersProfileFormData = {
|
||||||
background: {
|
background: {
|
||||||
educations: [],
|
educations: [],
|
||||||
experiences: [{ jobType: JobType.FULLTIME }],
|
experiences: [{ jobType: JobType.FULLTIME }],
|
||||||
@@ -116,7 +115,7 @@ export default function OffersSubmissionForm({
|
|||||||
const {
|
const {
|
||||||
handleSubmit,
|
handleSubmit,
|
||||||
trigger,
|
trigger,
|
||||||
formState: { isSubmitting },
|
formState: { isSubmitting, isDirty },
|
||||||
} = formMethods;
|
} = formMethods;
|
||||||
|
|
||||||
const generateAnalysisMutation = trpc.useMutation(
|
const generateAnalysisMutation = trpc.useMutation(
|
||||||
@@ -218,7 +217,7 @@ export default function OffersSubmissionForm({
|
|||||||
offer.monthYearReceived.year,
|
offer.monthYearReceived.year,
|
||||||
offer.monthYearReceived.month - 1, // Convert month to monthIndex
|
offer.monthYearReceived.month - 1, // Convert month to monthIndex
|
||||||
),
|
),
|
||||||
}));
|
})) as Array<OfferPostData>;
|
||||||
|
|
||||||
if (params.profileId && params.token) {
|
if (params.profileId && params.token) {
|
||||||
createOrUpdateMutation.mutate({
|
createOrUpdateMutation.mutate({
|
||||||
@@ -254,11 +253,14 @@ export default function OffersSubmissionForm({
|
|||||||
const warningText =
|
const warningText =
|
||||||
'Leave this page? Changes that you made will not be saved.';
|
'Leave this page? Changes that you made will not be saved.';
|
||||||
const handleWindowClose = (e: BeforeUnloadEvent) => {
|
const handleWindowClose = (e: BeforeUnloadEvent) => {
|
||||||
|
if (!isDirty) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
return (e.returnValue = warningText);
|
return (e.returnValue = warningText);
|
||||||
};
|
};
|
||||||
const handleRouteChange = (url: string) => {
|
const handleRouteChange = (url: string) => {
|
||||||
if (url.includes('/offers/submit/result')) {
|
if (url.includes('/offers/submit/result') || !isDirty) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (window.confirm(warningText)) {
|
if (window.confirm(warningText)) {
|
||||||
@@ -274,7 +276,7 @@ export default function OffersSubmissionForm({
|
|||||||
router.events.off('routeChangeStart', handleRouteChange);
|
router.events.off('routeChangeStart', handleRouteChange);
|
||||||
};
|
};
|
||||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||||
}, []);
|
}, [isDirty]);
|
||||||
|
|
||||||
return generateAnalysisMutation.isLoading ? (
|
return generateAnalysisMutation.isLoading ? (
|
||||||
<Spinner className="m-10" display="block" size="lg" />
|
<Spinner className="m-10" display="block" size="lg" />
|
||||||
@@ -353,4 +355,4 @@ export default function OffersSubmissionForm({
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -117,6 +117,7 @@ function FullTimeOfferDetailsForm({
|
|||||||
onSelect={(option) => {
|
onSelect={(option) => {
|
||||||
if (option) {
|
if (option) {
|
||||||
setValue(`offers.${index}.companyId`, option.value);
|
setValue(`offers.${index}.companyId`, option.value);
|
||||||
|
setValue(`offers.${index}.companyName`, option.label);
|
||||||
}
|
}
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
@@ -550,7 +551,6 @@ export default function OfferDetailsForm() {
|
|||||||
if (newJobType === jobType) {
|
if (newJobType === jobType) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
setDialogOpen(true);
|
setDialogOpen(true);
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
@@ -586,4 +586,4 @@ export default function OfferDetailsForm() {
|
|||||||
</Dialog>
|
</Dialog>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -131,9 +131,8 @@ function ProfileAnalysis({
|
|||||||
{isEditable && (
|
{isEditable && (
|
||||||
<div className="flex justify-end">
|
<div className="flex justify-end">
|
||||||
<Button
|
<Button
|
||||||
addonPosition="start"
|
|
||||||
icon={ArrowPathIcon}
|
icon={ArrowPathIcon}
|
||||||
label="Regenerate Analysis"
|
label="Regenerate analysis"
|
||||||
variant="secondary"
|
variant="secondary"
|
||||||
onClick={() => generateAnalysisMutation.mutate({ profileId })}
|
onClick={() => generateAnalysisMutation.mutate({ profileId })}
|
||||||
/>
|
/>
|
||||||
@@ -188,4 +187,4 @@ export default function ProfileDetails({
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -4,18 +4,101 @@ import type { MonthYear } from '~/components/shared/MonthYearPicker';
|
|||||||
|
|
||||||
import type { Location } from '~/types/offers';
|
import type { Location } from '~/types/offers';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Form data types
|
||||||
|
*/
|
||||||
|
|
||||||
|
export type OffersProfileFormData = {
|
||||||
|
background: BackgroundFormData;
|
||||||
|
id?: string;
|
||||||
|
offers: Array<OfferFormData>;
|
||||||
|
};
|
||||||
|
|
||||||
|
export type BackgroundFormData = {
|
||||||
|
educations: Array<EducationFormData>;
|
||||||
|
experiences: Array<ExperienceFormData>;
|
||||||
|
id?: string;
|
||||||
|
specificYoes: Array<SpecificYoeFormData>;
|
||||||
|
totalYoe: number;
|
||||||
|
};
|
||||||
|
|
||||||
|
type EducationFormData = {
|
||||||
|
endDate?: Date | null;
|
||||||
|
field?: string | null;
|
||||||
|
school?: string | null;
|
||||||
|
startDate?: Date | null;
|
||||||
|
type?: string | null;
|
||||||
|
};
|
||||||
|
|
||||||
|
type ExperienceFormData = {
|
||||||
|
cityId?: string | null;
|
||||||
|
cityName?: string | null;
|
||||||
|
companyId?: string | null;
|
||||||
|
companyName?: string | null;
|
||||||
|
durationInMonths?: number | null;
|
||||||
|
id?: string;
|
||||||
|
jobType?: string | null;
|
||||||
|
level?: string | null;
|
||||||
|
monthlySalary?: MoneyFormData | null;
|
||||||
|
title?: string | null;
|
||||||
|
totalCompensation?: MoneyFormData | null;
|
||||||
|
totalCompensationId?: string | null;
|
||||||
|
};
|
||||||
|
|
||||||
|
type SpecificYoeFormData = {
|
||||||
|
domain: string;
|
||||||
|
id?: string;
|
||||||
|
yoe: number;
|
||||||
|
};
|
||||||
|
|
||||||
|
export type OfferFormData = {
|
||||||
|
cityId: string;
|
||||||
|
cityName?: string;
|
||||||
|
comments: string;
|
||||||
|
companyId: string;
|
||||||
|
companyName?: string;
|
||||||
|
id?: string;
|
||||||
|
jobType: JobType;
|
||||||
|
monthYearReceived: MonthYear;
|
||||||
|
negotiationStrategy: string;
|
||||||
|
offersFullTime?: OfferFullTimeFormData | null;
|
||||||
|
offersIntern?: OfferInternFormData | null;
|
||||||
|
};
|
||||||
|
|
||||||
|
export type OfferFullTimeFormData = {
|
||||||
|
baseSalary?: MoneyFormData | null;
|
||||||
|
bonus?: MoneyFormData | null;
|
||||||
|
id?: string;
|
||||||
|
level: string;
|
||||||
|
stocks?: MoneyFormData | null;
|
||||||
|
title: string;
|
||||||
|
totalCompensation: MoneyFormData;
|
||||||
|
};
|
||||||
|
|
||||||
|
export type OfferInternFormData = {
|
||||||
|
id?: string;
|
||||||
|
internshipCycle: string;
|
||||||
|
monthlySalary: MoneyFormData;
|
||||||
|
startYear: number;
|
||||||
|
title: string;
|
||||||
|
};
|
||||||
|
|
||||||
|
type MoneyFormData = {
|
||||||
|
currency: string;
|
||||||
|
id?: string;
|
||||||
|
value?: number;
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Post request data types
|
||||||
|
*/
|
||||||
|
|
||||||
export type OffersProfilePostData = {
|
export type OffersProfilePostData = {
|
||||||
background: BackgroundPostData;
|
background: BackgroundPostData;
|
||||||
id?: string;
|
id?: string;
|
||||||
offers: Array<OfferPostData>;
|
offers: Array<OfferPostData>;
|
||||||
};
|
};
|
||||||
|
|
||||||
export type OffersProfileFormData = {
|
|
||||||
background: BackgroundPostData;
|
|
||||||
id?: string;
|
|
||||||
offers: Array<OfferFormData>;
|
|
||||||
};
|
|
||||||
|
|
||||||
export type BackgroundPostData = {
|
export type BackgroundPostData = {
|
||||||
educations: Array<EducationPostData>;
|
educations: Array<EducationPostData>;
|
||||||
experiences: Array<ExperiencePostData>;
|
experiences: Array<ExperiencePostData>;
|
||||||
@@ -24,6 +107,8 @@ export type BackgroundPostData = {
|
|||||||
totalYoe: number;
|
totalYoe: number;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
type EducationPostData = EducationFormData;
|
||||||
|
|
||||||
type ExperiencePostData = {
|
type ExperiencePostData = {
|
||||||
cityId?: string | null;
|
cityId?: string | null;
|
||||||
cityName?: string | null;
|
cityName?: string | null;
|
||||||
@@ -39,47 +124,26 @@ type ExperiencePostData = {
|
|||||||
totalCompensationId?: string | null;
|
totalCompensationId?: string | null;
|
||||||
};
|
};
|
||||||
|
|
||||||
type EducationPostData = {
|
type SpecificYoePostData = SpecificYoeFormData;
|
||||||
endDate?: Date | null;
|
|
||||||
field?: string | null;
|
|
||||||
id?: string;
|
|
||||||
school?: string | null;
|
|
||||||
startDate?: Date | null;
|
|
||||||
type?: string | null;
|
|
||||||
};
|
|
||||||
|
|
||||||
type SpecificYoePostData = {
|
|
||||||
domain: string;
|
|
||||||
id?: string;
|
|
||||||
yoe: number;
|
|
||||||
};
|
|
||||||
|
|
||||||
type SpecificYoe = SpecificYoePostData;
|
|
||||||
|
|
||||||
export type OfferPostData = {
|
export type OfferPostData = {
|
||||||
cityId: string;
|
cityId: string;
|
||||||
cityName?: string;
|
|
||||||
comments: string;
|
comments: string;
|
||||||
companyId: string;
|
companyId: string;
|
||||||
companyName?: string;
|
|
||||||
id?: string;
|
id?: string;
|
||||||
jobType: JobType;
|
jobType: JobType;
|
||||||
monthYearReceived: Date;
|
monthYearReceived: Date;
|
||||||
negotiationStrategy: string;
|
negotiationStrategy: string;
|
||||||
offersFullTime?: OfferFullTimePostData | null;
|
offersFullTime?: OfferFullTimePostData;
|
||||||
offersIntern?: OfferInternPostData | null;
|
offersIntern?: OfferInternPostData;
|
||||||
};
|
|
||||||
|
|
||||||
export type OfferFormData = Omit<OfferPostData, 'monthYearReceived'> & {
|
|
||||||
monthYearReceived: MonthYear;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
export type OfferFullTimePostData = {
|
export type OfferFullTimePostData = {
|
||||||
baseSalary: Money | null;
|
baseSalary: Money;
|
||||||
bonus: Money | null;
|
bonus: Money;
|
||||||
id?: string;
|
id?: string;
|
||||||
level: string;
|
level: string;
|
||||||
stocks: Money | null;
|
stocks: Money;
|
||||||
title: string;
|
title: string;
|
||||||
totalCompensation: Money;
|
totalCompensation: Money;
|
||||||
};
|
};
|
||||||
@@ -98,6 +162,10 @@ export type Money = {
|
|||||||
value: number;
|
value: number;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Display data types
|
||||||
|
*/
|
||||||
|
|
||||||
export type EducationDisplayData = {
|
export type EducationDisplayData = {
|
||||||
endDate?: string | null;
|
endDate?: string | null;
|
||||||
field?: string | null;
|
field?: string | null;
|
||||||
@@ -128,7 +196,7 @@ export type BackgroundDisplayData = {
|
|||||||
educations: Array<EducationDisplayData>;
|
educations: Array<EducationDisplayData>;
|
||||||
experiences: Array<OfferDisplayData>;
|
experiences: Array<OfferDisplayData>;
|
||||||
profileName: string;
|
profileName: string;
|
||||||
specificYoes: Array<SpecificYoe>;
|
specificYoes: Array<SpecificYoePostData>;
|
||||||
totalYoe: number;
|
totalYoe: number;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -3,7 +3,9 @@ import { useRouter } from 'next/router';
|
|||||||
import { useState } from 'react';
|
import { useState } from 'react';
|
||||||
import { JobType } from '@prisma/client';
|
import { JobType } from '@prisma/client';
|
||||||
|
|
||||||
import OffersSubmissionForm from '~/components/offers/offersSubmission/OffersSubmissionForm';
|
import OffersSubmissionForm, {
|
||||||
|
DEFAULT_CURRENCY,
|
||||||
|
} from '~/components/offers/offersSubmission/OffersSubmissionForm';
|
||||||
import type { OffersProfileFormData } from '~/components/offers/types';
|
import type { OffersProfileFormData } from '~/components/offers/types';
|
||||||
|
|
||||||
import { Spinner } from '~/../../../packages/ui/dist';
|
import { Spinner } from '~/../../../packages/ui/dist';
|
||||||
@@ -44,9 +46,16 @@ export default function OffersEditPage() {
|
|||||||
id: exp.id,
|
id: exp.id,
|
||||||
jobType: exp.jobType,
|
jobType: exp.jobType,
|
||||||
level: exp.level,
|
level: exp.level,
|
||||||
monthlySalary: exp.monthlySalary,
|
monthlySalary: {
|
||||||
|
currency: exp.monthlySalary?.currency || DEFAULT_CURRENCY,
|
||||||
|
value: exp.monthlySalary?.value,
|
||||||
|
},
|
||||||
title: exp.title,
|
title: exp.title,
|
||||||
totalCompensation: exp.totalCompensation,
|
totalCompensation: {
|
||||||
|
currency:
|
||||||
|
exp.totalCompensation?.currency || DEFAULT_CURRENCY,
|
||||||
|
value: exp.totalCompensation?.value,
|
||||||
|
},
|
||||||
})),
|
})),
|
||||||
id,
|
id,
|
||||||
specificYoes,
|
specificYoes,
|
||||||
|
|||||||
Reference in New Issue
Block a user