mirror of
https://github.com/yangshun/tech-interview-handbook.git
synced 2025-07-28 04:33:42 +08:00
[offers][fix] Fix offers form experience section (#427)
This commit is contained in:
@ -1,15 +1,9 @@
|
||||
import { useRouter } from 'next/router';
|
||||
// Import { useState } from 'react';
|
||||
// import { setTimeout } from 'timers';
|
||||
import { DocumentDuplicateIcon } from '@heroicons/react/20/solid';
|
||||
import { EyeIcon } from '@heroicons/react/24/outline';
|
||||
import { Button, TextInput, useToast } from '@tih/ui';
|
||||
|
||||
import {
|
||||
copyProfileLink,
|
||||
getProfileLink,
|
||||
getProfilePath,
|
||||
} from '~/utils/offers/link';
|
||||
import { copyProfileLink, getProfileLink } from '~/utils/offers/link';
|
||||
|
||||
type OfferProfileSaveProps = Readonly<{
|
||||
profileId: string;
|
||||
@ -23,7 +17,6 @@ export default function OffersProfileSave({
|
||||
const { showToast } = useToast();
|
||||
// Const [isSaving, setSaving] = useState(false);
|
||||
// const [isSaved, setSaved] = useState(false);
|
||||
const router = useRouter();
|
||||
|
||||
// Const saveProfile = () => {
|
||||
// setSaving(true);
|
||||
@ -82,14 +75,6 @@ export default function OffersProfileSave({
|
||||
onClick={saveProfile}
|
||||
/>
|
||||
</div> */}
|
||||
<div>
|
||||
<Button
|
||||
icon={EyeIcon}
|
||||
label="View your profile"
|
||||
variant="special"
|
||||
onClick={() => router.push(getProfilePath(profileId, token))}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
|
@ -0,0 +1,49 @@
|
||||
import { useRouter } from 'next/router';
|
||||
import { EyeIcon } from '@heroicons/react/24/outline';
|
||||
|
||||
import { Button } from '~/../../../packages/ui/dist';
|
||||
import { getProfilePath } from '~/utils/offers/link';
|
||||
|
||||
import OfferAnalysis from '../offerAnalysis/OfferAnalysis';
|
||||
|
||||
import type { ProfileAnalysis } from '~/types/offers';
|
||||
|
||||
type Props = Readonly<{
|
||||
analysis?: ProfileAnalysis | null;
|
||||
isError: boolean;
|
||||
isLoading: boolean;
|
||||
profileId?: string;
|
||||
token?: string;
|
||||
}>;
|
||||
|
||||
export default function OffersSubmissionAnalysis({
|
||||
analysis,
|
||||
isError,
|
||||
isLoading,
|
||||
profileId = '',
|
||||
token = '',
|
||||
}: Props) {
|
||||
const router = useRouter();
|
||||
|
||||
return (
|
||||
<div>
|
||||
<h5 className="mb-8 text-center text-4xl font-bold text-slate-900">
|
||||
Result
|
||||
</h5>
|
||||
<OfferAnalysis
|
||||
key={3}
|
||||
allAnalysis={analysis}
|
||||
isError={isError}
|
||||
isLoading={isLoading}
|
||||
/>
|
||||
<div className="mt-8 text-center">
|
||||
<Button
|
||||
icon={EyeIcon}
|
||||
label="View your profile"
|
||||
variant="special"
|
||||
onClick={() => router.push(getProfilePath(profileId, token))}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
@ -15,16 +15,17 @@ import type {
|
||||
} from '~/components/offers/types';
|
||||
import type { Month } from '~/components/shared/MonthYearPicker';
|
||||
|
||||
import { cleanObject, removeInvalidMoneyData } from '~/utils/offers/form';
|
||||
import {
|
||||
cleanObject,
|
||||
removeEmptyObjects,
|
||||
removeInvalidMoneyData,
|
||||
} from '~/utils/offers/form';
|
||||
import { getCurrentMonth, getCurrentYear } from '~/utils/offers/time';
|
||||
import { trpc } from '~/utils/trpc';
|
||||
|
||||
import OfferAnalysis from '../offerAnalysis/OfferAnalysis';
|
||||
import OffersSubmissionAnalysis from './OffersSubmissionAnalysis';
|
||||
|
||||
import type {
|
||||
CreateOfferProfileResponse,
|
||||
ProfileAnalysis,
|
||||
} from '~/types/offers';
|
||||
import type { ProfileAnalysis } from '~/types/offers';
|
||||
|
||||
const defaultOfferValues = {
|
||||
comments: '',
|
||||
@ -73,15 +74,12 @@ type Props = Readonly<{
|
||||
|
||||
export default function OffersSubmissionForm({
|
||||
initialOfferProfileValues = defaultOfferProfileValues,
|
||||
profileId,
|
||||
token,
|
||||
profileId: editProfileId = '',
|
||||
token: editToken = '',
|
||||
}: Props) {
|
||||
const [formStep, setFormStep] = useState(0);
|
||||
const [createProfileResponse, setCreateProfileResponse] =
|
||||
useState<CreateOfferProfileResponse>({
|
||||
id: profileId || '',
|
||||
token: token || '',
|
||||
});
|
||||
const [profileId, setProfileId] = useState(editProfileId);
|
||||
const [token, setToken] = useState(editToken);
|
||||
const [analysis, setAnalysis] = useState<ProfileAnalysis | null>(null);
|
||||
|
||||
const pageRef = useRef<HTMLDivElement>(null);
|
||||
@ -125,11 +123,7 @@ export default function OffersSubmissionForm({
|
||||
},
|
||||
{
|
||||
component: (
|
||||
<OffersProfileSave
|
||||
key={2}
|
||||
profileId={createProfileResponse.id || ''}
|
||||
token={createProfileResponse.token}
|
||||
/>
|
||||
<OffersProfileSave key={2} profileId={profileId} token={token} />
|
||||
),
|
||||
hasNext: true,
|
||||
hasPrevious: false,
|
||||
@ -137,17 +131,13 @@ export default function OffersSubmissionForm({
|
||||
},
|
||||
{
|
||||
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>
|
||||
<OffersSubmissionAnalysis
|
||||
analysis={analysis}
|
||||
isError={generateAnalysisMutation.isError}
|
||||
isLoading={generateAnalysisMutation.isLoading}
|
||||
profileId={profileId}
|
||||
token={token}
|
||||
/>
|
||||
),
|
||||
hasNext: false,
|
||||
hasPrevious: true,
|
||||
@ -184,7 +174,8 @@ export default function OffersSubmissionForm({
|
||||
generateAnalysisMutation.mutate({
|
||||
profileId: data?.id || '',
|
||||
});
|
||||
setCreateProfileResponse(data);
|
||||
setProfileId(data.id);
|
||||
setToken(data.token);
|
||||
setFormStep(formStep + 1);
|
||||
scrollToTop();
|
||||
},
|
||||
@ -197,6 +188,7 @@ export default function OffersSubmissionForm({
|
||||
}
|
||||
|
||||
data = removeInvalidMoneyData(data);
|
||||
data.offers = removeEmptyObjects(data.offers);
|
||||
|
||||
const background = cleanObject(data.background);
|
||||
background.specificYoes = data.background.specificYoes.filter(
|
||||
|
@ -18,7 +18,6 @@ import {
|
||||
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';
|
||||
@ -235,7 +234,6 @@ function InternshipJobFields() {
|
||||
function CurrentJobSection() {
|
||||
const { register } = useFormContext();
|
||||
const watchJobType = useWatch({
|
||||
defaultValue: JobType.FULLTIME,
|
||||
name: 'background.experiences.0.jobType',
|
||||
});
|
||||
|
||||
@ -247,7 +245,7 @@ function CurrentJobSection() {
|
||||
<div className="mb-5 rounded-lg border border-slate-200 px-10 py-5">
|
||||
<div className="mb-5">
|
||||
<FormRadioList
|
||||
defaultValue={JobType.FULLTIME}
|
||||
defaultValue={watchJobType}
|
||||
isLabelHidden={true}
|
||||
label="Job Type"
|
||||
orientation="horizontal"
|
||||
@ -306,22 +304,6 @@ 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>
|
||||
</>
|
||||
|
@ -34,7 +34,17 @@ export default function OffersEditPage() {
|
||||
experiences:
|
||||
experiences.length === 0
|
||||
? [{ jobType: JobType.FULLTIME }]
|
||||
: experiences,
|
||||
: experiences.map((exp) => ({
|
||||
companyId: exp.company?.id,
|
||||
durationInMonths: exp.durationInMonths,
|
||||
id: exp.id,
|
||||
jobType: exp.jobType,
|
||||
level: exp.level,
|
||||
location: exp.location,
|
||||
monthlySalary: exp.monthlySalary,
|
||||
title: exp.title,
|
||||
totalCompensation: exp.totalCompensation,
|
||||
})),
|
||||
id,
|
||||
specificYoes,
|
||||
totalYoe,
|
||||
|
@ -285,11 +285,7 @@ export const offersProfileRouter = createRouter()
|
||||
},
|
||||
experiences: {
|
||||
create: input.background.experiences.map(async (x) => {
|
||||
if (
|
||||
x.jobType === JobType.FULLTIME &&
|
||||
x.totalCompensation?.currency != null &&
|
||||
x.totalCompensation?.value != null
|
||||
) {
|
||||
if (x.jobType === JobType.FULLTIME) {
|
||||
if (x.companyId) {
|
||||
return {
|
||||
company: {
|
||||
@ -301,18 +297,21 @@ export const offersProfileRouter = createRouter()
|
||||
jobType: x.jobType,
|
||||
level: x.level,
|
||||
title: x.title,
|
||||
totalCompensation: {
|
||||
create: {
|
||||
baseCurrency: baseCurrencyString,
|
||||
baseValue: await convert(
|
||||
x.totalCompensation.value,
|
||||
x.totalCompensation.currency,
|
||||
baseCurrencyString,
|
||||
),
|
||||
currency: x.totalCompensation.currency,
|
||||
value: x.totalCompensation.value,
|
||||
},
|
||||
},
|
||||
totalCompensation:
|
||||
x.totalCompensation != null
|
||||
? {
|
||||
create: {
|
||||
baseCurrency: baseCurrencyString,
|
||||
baseValue: await convert(
|
||||
x.totalCompensation.value,
|
||||
x.totalCompensation.currency,
|
||||
baseCurrencyString,
|
||||
),
|
||||
currency: x.totalCompensation.currency,
|
||||
value: x.totalCompensation.value,
|
||||
},
|
||||
}
|
||||
: undefined,
|
||||
};
|
||||
}
|
||||
return {
|
||||
@ -321,25 +320,24 @@ export const offersProfileRouter = createRouter()
|
||||
level: x.level,
|
||||
location: x.location,
|
||||
title: x.title,
|
||||
totalCompensation: {
|
||||
create: {
|
||||
baseCurrency: baseCurrencyString,
|
||||
baseValue: await convert(
|
||||
x.totalCompensation.value,
|
||||
x.totalCompensation.currency,
|
||||
baseCurrencyString,
|
||||
),
|
||||
currency: x.totalCompensation.currency,
|
||||
value: x.totalCompensation.value,
|
||||
},
|
||||
},
|
||||
totalCompensation:
|
||||
x.totalCompensation != null
|
||||
? {
|
||||
create: {
|
||||
baseCurrency: baseCurrencyString,
|
||||
baseValue: await convert(
|
||||
x.totalCompensation.value,
|
||||
x.totalCompensation.currency,
|
||||
baseCurrencyString,
|
||||
),
|
||||
currency: x.totalCompensation.currency,
|
||||
value: x.totalCompensation.value,
|
||||
},
|
||||
}
|
||||
: undefined,
|
||||
};
|
||||
}
|
||||
if (
|
||||
x.jobType === JobType.INTERN &&
|
||||
x.monthlySalary?.currency != null &&
|
||||
x.monthlySalary?.value != null
|
||||
) {
|
||||
if (x.jobType === JobType.INTERN) {
|
||||
if (x.companyId) {
|
||||
return {
|
||||
company: {
|
||||
@ -349,36 +347,42 @@ export const offersProfileRouter = createRouter()
|
||||
},
|
||||
durationInMonths: x.durationInMonths,
|
||||
jobType: x.jobType,
|
||||
monthlySalary: {
|
||||
create: {
|
||||
baseCurrency: baseCurrencyString,
|
||||
baseValue: await convert(
|
||||
x.monthlySalary.value,
|
||||
x.monthlySalary.currency,
|
||||
baseCurrencyString,
|
||||
),
|
||||
currency: x.monthlySalary.currency,
|
||||
value: x.monthlySalary.value,
|
||||
},
|
||||
},
|
||||
monthlySalary:
|
||||
x.monthlySalary != null
|
||||
? {
|
||||
create: {
|
||||
baseCurrency: baseCurrencyString,
|
||||
baseValue: await convert(
|
||||
x.monthlySalary.value,
|
||||
x.monthlySalary.currency,
|
||||
baseCurrencyString,
|
||||
),
|
||||
currency: x.monthlySalary.currency,
|
||||
value: x.monthlySalary.value,
|
||||
},
|
||||
}
|
||||
: undefined,
|
||||
title: x.title,
|
||||
};
|
||||
}
|
||||
return {
|
||||
durationInMonths: x.durationInMonths,
|
||||
jobType: x.jobType,
|
||||
monthlySalary: {
|
||||
create: {
|
||||
baseCurrency: baseCurrencyString,
|
||||
baseValue: await convert(
|
||||
x.monthlySalary.value,
|
||||
x.monthlySalary.currency,
|
||||
baseCurrencyString,
|
||||
),
|
||||
currency: x.monthlySalary.currency,
|
||||
value: x.monthlySalary.value,
|
||||
},
|
||||
},
|
||||
monthlySalary:
|
||||
x.monthlySalary != null
|
||||
? {
|
||||
create: {
|
||||
baseCurrency: baseCurrencyString,
|
||||
baseValue: await convert(
|
||||
x.monthlySalary.value,
|
||||
x.monthlySalary.currency,
|
||||
baseCurrencyString,
|
||||
),
|
||||
currency: x.monthlySalary.currency,
|
||||
value: x.monthlySalary.value,
|
||||
},
|
||||
}
|
||||
: undefined,
|
||||
title: x.title,
|
||||
};
|
||||
}
|
||||
@ -710,6 +714,7 @@ export const offersProfileRouter = createRouter()
|
||||
data: {
|
||||
companyId: exp.companyId, // TODO: check if can change with connect or whether there is a difference
|
||||
durationInMonths: exp.durationInMonths,
|
||||
jobType: exp.jobType as JobType,
|
||||
level: exp.level,
|
||||
},
|
||||
where: {
|
||||
@ -818,18 +823,20 @@ export const offersProfileRouter = createRouter()
|
||||
level: exp.level,
|
||||
location: exp.location,
|
||||
title: exp.title,
|
||||
totalCompensation: {
|
||||
create: {
|
||||
baseCurrency: baseCurrencyString,
|
||||
baseValue: await convert(
|
||||
exp.totalCompensation.value,
|
||||
exp.totalCompensation.currency,
|
||||
baseCurrencyString,
|
||||
),
|
||||
currency: exp.totalCompensation.currency,
|
||||
value: exp.totalCompensation.value,
|
||||
},
|
||||
},
|
||||
totalCompensation: exp.totalCompensation
|
||||
? {
|
||||
create: {
|
||||
baseCurrency: baseCurrencyString,
|
||||
baseValue: await convert(
|
||||
exp.totalCompensation.value,
|
||||
exp.totalCompensation.currency,
|
||||
baseCurrencyString,
|
||||
),
|
||||
currency: exp.totalCompensation.currency,
|
||||
value: exp.totalCompensation.value,
|
||||
},
|
||||
}
|
||||
: undefined,
|
||||
},
|
||||
},
|
||||
},
|
||||
|
@ -32,6 +32,33 @@ export function cleanObject(object: any) {
|
||||
return object;
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes empty objects from an object.
|
||||
* @param object
|
||||
* @returns object without empty values or objects.
|
||||
*/
|
||||
export function removeEmptyObjects(object: any) {
|
||||
Object.entries(object).forEach(([k, v]) => {
|
||||
if ((v && typeof v === 'object') || Array.isArray(v)) {
|
||||
removeEmptyObjects(v);
|
||||
}
|
||||
if (
|
||||
v &&
|
||||
typeof v === 'object' &&
|
||||
!Object.keys(v).length &&
|
||||
!Array.isArray(v)
|
||||
) {
|
||||
if (Array.isArray(object)) {
|
||||
const index = object.indexOf(v);
|
||||
object.splice(index, 1);
|
||||
} else if (!(v instanceof Date)) {
|
||||
delete object[k];
|
||||
}
|
||||
}
|
||||
});
|
||||
return object;
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes invalid money data from an object.
|
||||
* If currency is present but value is not present, money object is removed.
|
||||
|
Reference in New Issue
Block a user