[offers][fix] Use title typeahead, add default currency and remove specialization field (#423)

This commit is contained in:
Ai Ling
2022-10-24 22:34:28 +08:00
committed by GitHub
parent 64cd69d024
commit bf35f97961
7 changed files with 120 additions and 136 deletions

View File

@ -2,26 +2,6 @@ import { EducationBackgroundType } from './types';
export const emptyOption = '----';
// TODO: use enums
export const titleOptions = [
{
label: 'Software Engineer',
value: 'Software Engineer',
},
{
label: 'Frontend Engineer',
value: 'Frontend Engineer',
},
{
label: 'Backend Engineer',
value: 'Backend Engineer',
},
{
label: 'Full-stack Engineer',
value: 'Full-stack Engineer',
},
];
export const locationOptions = [
{
label: 'Singapore, Singapore',

View File

@ -115,7 +115,7 @@ export default function OffersSubmissionForm({
),
hasNext: true,
hasPrevious: false,
label: 'Offer details',
label: 'Offers',
},
{
component: <BackgroundForm key={1} />,
@ -123,30 +123,35 @@ export default function OffersSubmissionForm({
hasPrevious: true,
label: 'Background',
},
{
component: (
<OfferAnalysis
key={2}
allAnalysis={analysis}
isError={generateAnalysisMutation.isError}
isLoading={generateAnalysisMutation.isLoading}
/>
),
hasNext: true,
hasPrevious: false,
label: 'Analysis',
},
{
component: (
<OffersProfileSave
key={3}
key={2}
profileId={createProfileResponse.id || ''}
token={createProfileResponse.token}
/>
),
hasNext: false,
hasNext: true,
hasPrevious: false,
label: 'Save',
label: 'Save profile',
},
{
component: (
<div>
<h5 className="mb-8 text-center text-4xl font-bold text-slate-900">
Result
</h5>
<OfferAnalysis
key={3}
allAnalysis={analysis}
isError={generateAnalysisMutation.isError}
isLoading={generateAnalysisMutation.isLoading}
/>
</div>
),
hasNext: false,
hasPrevious: true,
label: 'Analysis',
},
];
@ -231,7 +236,7 @@ export default function OffersSubmissionForm({
<FormProvider {...formMethods}>
<form onSubmit={handleSubmit(onSubmit)}>
{formSteps[formStep].component}
{/* <pre>{JSON.stringify(formMethods.watch(), null, 2)}</pre> */}
<pre>{JSON.stringify(formMethods.watch(), null, 2)}</pre>
{formSteps[formStep].hasNext && (
<div className="flex justify-end">
<Button

View File

@ -8,13 +8,17 @@ import {
emptyOption,
FieldError,
locationOptions,
titleOptions,
} from '~/components/offers/constants';
import type { BackgroundPostData } from '~/components/offers/types';
import CompaniesTypeahead from '~/components/shared/CompaniesTypeahead';
import JobTitlesTypeahead from '~/components/shared/JobTitlesTypahead';
import { CURRENCY_OPTIONS } from '~/utils/offers/currency/CurrencyEnum';
import {
Currency,
CURRENCY_OPTIONS,
} from '~/utils/offers/currency/CurrencyEnum';
import FormMonthYearPicker from '../../forms/FormMonthYearPicker';
import FormRadioList from '../../forms/FormRadioList';
import FormSelect from '../../forms/FormSelect';
import FormTextInput from '../../forms/FormTextInput';
@ -92,13 +96,13 @@ function FullTimeJobFields() {
return (
<>
<div className="mb-5 grid grid-cols-2 space-x-3">
<FormSelect
display="block"
label="Title"
options={titleOptions}
placeholder={emptyOption}
{...register(`background.experiences.0.title`)}
/>
<div>
<JobTitlesTypeahead
onSelect={({ value }) =>
setValue(`background.experiences.0.title`, value)
}
/>
</div>
<div>
<CompaniesTypeahead
onSelect={({ value }) =>
@ -112,6 +116,7 @@ function FullTimeJobFields() {
endAddOn={
<FormSelect
borderStyle="borderless"
defaultValue={Currency.SGD}
isLabelHidden={true}
label="Currency"
options={CURRENCY_OPTIONS}
@ -177,13 +182,13 @@ function InternshipJobFields() {
return (
<>
<div className="mb-5 grid grid-cols-2 space-x-3">
<FormSelect
display="block"
label="Title"
options={titleOptions}
placeholder={emptyOption}
{...register(`background.experiences.0.title`)}
/>
<div>
<JobTitlesTypeahead
onSelect={({ value }) =>
setValue(`background.experiences.0.title`, value)
}
/>
</div>
<div>
<CompaniesTypeahead
onSelect={({ value }) =>
@ -197,6 +202,7 @@ function InternshipJobFields() {
endAddOn={
<FormSelect
borderStyle="borderless"
defaultValue={Currency.SGD}
isLabelHidden={true}
label="Currency"
options={CURRENCY_OPTIONS}
@ -310,6 +316,22 @@ function EducationSection() {
{...register(`background.educations.0.school`)}
/>
</div>
<div className="grid grid-cols-2 space-x-3">
<FormMonthYearPicker
monthLabel="Candidature Start"
yearLabel=""
{...register(`background.educations.0.startDate`, {
required: FieldError.REQUIRED,
})}
/>
<FormMonthYearPicker
monthLabel="Candidature End"
yearLabel=""
{...register(`background.educations.0.endDate`, {
required: FieldError.REQUIRED,
})}
/>
</div>
</Collapsible>
</div>
</>
@ -319,13 +341,9 @@ function EducationSection() {
export default function BackgroundForm() {
return (
<div>
<h5 className="mb-2 text-center text-4xl font-bold text-slate-900">
<h5 className="mb-8 text-center text-4xl font-bold text-slate-900">
Help us better gauge your offers
</h5>
<h6 className="text-md mx-10 mb-8 text-center font-light text-slate-600">
This section is mostly optional, but your background information helps
us benchmark your offers.
</h6>
<div>
<YoeSection />
<CurrentJobSection />

View File

@ -13,6 +13,7 @@ import { JobType } from '@prisma/client';
import { Button, Dialog } from '@tih/ui';
import CompaniesTypeahead from '~/components/shared/CompaniesTypeahead';
import JobTitlesTypeahead from '~/components/shared/JobTitlesTypahead';
import {
defaultFullTimeOfferValues,
@ -23,7 +24,6 @@ import {
FieldError,
internshipCycleOptions,
locationOptions,
titleOptions,
yearOptions,
} from '../../constants';
import FormMonthYearPicker from '../../forms/FormMonthYearPicker';
@ -32,7 +32,10 @@ import FormTextArea from '../../forms/FormTextArea';
import FormTextInput from '../../forms/FormTextInput';
import type { OfferFormData } from '../../types';
import { JobTypeLabel } from '../../types';
import { CURRENCY_OPTIONS } from '../../../../utils/offers/currency/CurrencyEnum';
import {
Currency,
CURRENCY_OPTIONS,
} from '../../../../utils/offers/currency/CurrencyEnum';
type FullTimeOfferDetailsFormProps = Readonly<{
index: number;
@ -64,32 +67,11 @@ function FullTimeOfferDetailsForm({
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">
<FormSelect
display="block"
errorMessage={offerFields?.offersFullTime?.title?.message}
label="Title"
options={titleOptions}
placeholder={emptyOption}
required={true}
{...register(`offers.${index}.offersFullTime.title`, {
required: FieldError.REQUIRED,
})}
/>
<FormTextInput
errorMessage={offerFields?.offersFullTime?.specialization?.message}
label="Focus / Specialization"
placeholder="e.g. Front End"
required={true}
{...register(`offers.${index}.offersFullTime.specialization`, {
required: FieldError.REQUIRED,
})}
/>
</div>
<div className="mb-5 flex grid grid-cols-2 space-x-3">
<div>
<CompaniesTypeahead
<JobTitlesTypeahead
required={true}
onSelect={({ value }) =>
setValue(`offers.${index}.companyId`, value)
setValue(`offers.${index}.offersFullTime.title`, value)
}
/>
</div>
@ -103,7 +85,15 @@ function FullTimeOfferDetailsForm({
})}
/>
</div>
<div className="mb-5 flex grid grid-cols-2 items-start space-x-3">
<div className="mb-5 flex grid grid-cols-2 space-x-3">
<div>
<CompaniesTypeahead
required={true}
onSelect={({ value }) =>
setValue(`offers.${index}.companyId`, value)
}
/>
</div>
<FormSelect
display="block"
errorMessage={offerFields?.location?.message}
@ -115,6 +105,8 @@ function FullTimeOfferDetailsForm({
required: FieldError.REQUIRED,
})}
/>
</div>
<div className="mb-5 flex grid grid-cols-2 items-start space-x-3">
<FormMonthYearPicker
monthLabel="Date Received"
monthRequired={true}
@ -129,6 +121,7 @@ function FullTimeOfferDetailsForm({
endAddOn={
<FormSelect
borderStyle="borderless"
defaultValue={Currency.SGD}
isLabelHidden={true}
label="Currency"
options={CURRENCY_OPTIONS}
@ -165,14 +158,12 @@ function FullTimeOfferDetailsForm({
endAddOn={
<FormSelect
borderStyle="borderless"
defaultValue={Currency.SGD}
isLabelHidden={true}
label="Currency"
options={CURRENCY_OPTIONS}
{...register(
`offers.${index}.offersFullTime.baseSalary.currency`,
{
required: FieldError.REQUIRED,
},
)}
/>
}
@ -180,13 +171,11 @@ function FullTimeOfferDetailsForm({
errorMessage={offerFields?.offersFullTime?.baseSalary?.value?.message}
label="Base Salary (Annual)"
placeholder="0"
required={true}
startAddOn="$"
startAddOnType="label"
type="number"
{...register(`offers.${index}.offersFullTime.baseSalary.value`, {
min: { message: FieldError.NON_NEGATIVE_NUMBER, value: 0 },
required: FieldError.REQUIRED,
valueAsNumber: true,
})}
/>
@ -194,25 +183,22 @@ function FullTimeOfferDetailsForm({
endAddOn={
<FormSelect
borderStyle="borderless"
defaultValue={Currency.SGD}
isLabelHidden={true}
label="Currency"
options={CURRENCY_OPTIONS}
{...register(`offers.${index}.offersFullTime.bonus.currency`, {
required: FieldError.REQUIRED,
})}
{...register(`offers.${index}.offersFullTime.bonus.currency`)}
/>
}
endAddOnType="element"
errorMessage={offerFields?.offersFullTime?.bonus?.value?.message}
label="Bonus (Annual)"
placeholder="0"
required={true}
startAddOn="$"
startAddOnType="label"
type="number"
{...register(`offers.${index}.offersFullTime.bonus.value`, {
min: { message: FieldError.NON_NEGATIVE_NUMBER, value: 0 },
required: FieldError.REQUIRED,
valueAsNumber: true,
})}
/>
@ -222,25 +208,22 @@ function FullTimeOfferDetailsForm({
endAddOn={
<FormSelect
borderStyle="borderless"
defaultValue={Currency.SGD}
isLabelHidden={true}
label="Currency"
options={CURRENCY_OPTIONS}
{...register(`offers.${index}.offersFullTime.stocks.currency`, {
required: FieldError.REQUIRED,
})}
{...register(`offers.${index}.offersFullTime.stocks.currency`)}
/>
}
endAddOnType="element"
errorMessage={offerFields?.offersFullTime?.stocks?.value?.message}
label="Stocks (Annual)"
placeholder="0"
required={true}
startAddOn="$"
startAddOnType="label"
type="number"
{...register(`offers.${index}.offersFullTime.stocks.value`, {
min: { message: FieldError.NON_NEGATIVE_NUMBER, value: 0 },
required: FieldError.REQUIRED,
valueAsNumber: true,
})}
/>
@ -291,32 +274,19 @@ function InternshipOfferDetailsForm({
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">
<FormSelect
display="block"
errorMessage={offerFields?.offersIntern?.title?.message}
label="Title"
options={titleOptions}
placeholder={emptyOption}
required={true}
{...register(`offers.${index}.offersIntern.title`, {
minLength: 1,
required: FieldError.REQUIRED,
})}
/>
<FormTextInput
errorMessage={offerFields?.offersIntern?.specialization?.message}
label="Focus / Specialization"
placeholder="e.g. Front End"
required={true}
{...register(`offers.${index}.offersIntern.specialization`, {
minLength: 1,
required: FieldError.REQUIRED,
})}
/>
<div>
<JobTitlesTypeahead
required={true}
onSelect={({ value }) =>
setValue(`offers.${index}.offersIntern.title`, value)
}
/>
</div>
</div>
<div className="mb-5 grid grid-cols-2 space-x-3">
<div>
<CompaniesTypeahead
required={true}
onSelect={({ value }) =>
setValue(`offers.${index}.companyId`, value)
}
@ -374,6 +344,7 @@ function InternshipOfferDetailsForm({
endAddOn={
<FormSelect
borderStyle="borderless"
defaultValue={Currency.SGD}
isLabelHidden={true}
label="Currency"
options={CURRENCY_OPTIONS}

View File

@ -1,5 +1,8 @@
import Link from 'next/link';
import type { JobTitleType } from '~/components/shared/JobTitles';
import { getLabelForJobTitleType } from '~/components/shared/JobTitles';
import { convertMoneyToString } from '~/utils/offers/currency';
import { formatDate } from '~/utils/offers/time';
@ -19,7 +22,9 @@ export default function OfferTableRow({
scope="row">
{company.name}
</th>
<td className="py-4 px-6">{title}</td>
<td className="py-4 px-6">
{getLabelForJobTitleType(title as JobTitleType)}
</td>
<td className="py-4 px-6">{totalYoe}</td>
<td className="py-4 px-6">{convertMoneyToString(income)}</td>
<td className="py-4 px-6">{formatDate(monthYearReceived)}</td>

View File

@ -1,13 +1,12 @@
import { useState } from 'react';
import { Select } from '@tih/ui';
import { titleOptions } from '~/components/offers/constants';
import OffersTitle from '~/components/offers/OffersTitle';
import OffersTable from '~/components/offers/table/OffersTable';
import CompaniesTypeahead from '~/components/shared/CompaniesTypeahead';
import JobTitlesTypeahead from '~/components/shared/JobTitlesTypahead';
export default function OffersHomePage() {
const [jobTitleFilter, setjobTitleFilter] = useState('Software Engineer');
const [jobTitleFilter, setjobTitleFilter] = useState('software-engineer');
const [companyFilter, setCompanyFilter] = useState('');
return (
@ -18,19 +17,17 @@ export default function OffersHomePage() {
<div className="mt-4 flex items-center">
Viewing offers for
<div className="mx-4">
<Select
<JobTitlesTypeahead
isLabelHidden={true}
label="Select a job title"
options={titleOptions}
value={jobTitleFilter}
onChange={setjobTitleFilter}
placeHolder="Software Engineer"
onSelect={({ value }) => setjobTitleFilter(value)}
/>
</div>
in
<div className="ml-4">
<CompaniesTypeahead
isLabelHidden={true}
placeHolder="All companies"
placeHolder="All Companies"
onSelect={({ value }) => setCompanyFilter(value)}
/>
</div>

View File

@ -10,6 +10,8 @@ import type {
BackgroundDisplayData,
OfferDisplayData,
} from '~/components/offers/types';
import type { JobTitleType } from '~/components/shared/JobTitles';
import { getLabelForJobTitleType } from '~/components/shared/JobTitles';
import { useToast } from '~/../../../packages/ui/dist';
import { convertMoneyToString } from '~/utils/offers/currency';
@ -62,7 +64,9 @@ export default function OfferProfile() {
companyName: res.company.name,
id: res.offersFullTime.id,
jobLevel: res.offersFullTime.level,
jobTitle: res.offersFullTime.title,
jobTitle: getLabelForJobTitleType(
res.offersFullTime.title as JobTitleType,
),
location: res.location,
negotiationStrategy: res.negotiationStrategy,
otherComment: res.comments,
@ -77,7 +81,9 @@ export default function OfferProfile() {
const filteredOffer: OfferDisplayData = {
companyName: res.company.name,
id: res.offersIntern!.id,
jobTitle: res.offersIntern!.title,
jobTitle: getLabelForJobTitleType(
res.offersIntern!.title as JobTitleType,
),
location: res.location,
monthlySalary: convertMoneyToString(
res.offersIntern!.monthlySalary,
@ -107,7 +113,9 @@ export default function OfferProfile() {
companyName: experience.company?.name,
duration: experience.durationInMonths,
jobLevel: experience.level,
jobTitle: experience.title,
jobTitle: experience.title
? getLabelForJobTitleType(experience.title as JobTitleType)
: null,
monthlySalary: experience.monthlySalary
? convertMoneyToString(experience.monthlySalary)
: null,