mirror of
https://github.com/yangshun/tech-interview-handbook.git
synced 2026-05-02 06:21:20 +08:00
[offers][feat] Add form validation for typeaheads (#508)
This commit is contained in:
@@ -0,0 +1,37 @@
|
|||||||
|
import type { ComponentProps } from 'react';
|
||||||
|
import { useFormContext, useWatch } from 'react-hook-form';
|
||||||
|
|
||||||
|
import CitiesTypeahead from '~/components/shared/CitiesTypeahead';
|
||||||
|
|
||||||
|
type Props = Omit<
|
||||||
|
ComponentProps<typeof CitiesTypeahead>,
|
||||||
|
'onSelect' | 'value'
|
||||||
|
> & {
|
||||||
|
names: { label: string; value: string };
|
||||||
|
};
|
||||||
|
|
||||||
|
export default function FormCitiesTypeahead({ names, ...props }: Props) {
|
||||||
|
const { setValue } = useFormContext();
|
||||||
|
const watchCityId = useWatch({
|
||||||
|
name: names.value,
|
||||||
|
});
|
||||||
|
const watchCityName = useWatch({
|
||||||
|
name: names.label,
|
||||||
|
});
|
||||||
|
|
||||||
|
return (
|
||||||
|
<CitiesTypeahead
|
||||||
|
label="Location"
|
||||||
|
{...props}
|
||||||
|
value={{
|
||||||
|
id: watchCityId,
|
||||||
|
label: watchCityName,
|
||||||
|
value: watchCityId,
|
||||||
|
}}
|
||||||
|
onSelect={(option) => {
|
||||||
|
setValue(names.value, option?.value);
|
||||||
|
setValue(names.label, option?.value);
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
}
|
||||||
@@ -0,0 +1,36 @@
|
|||||||
|
import type { ComponentProps } from 'react';
|
||||||
|
import { useFormContext, useWatch } from 'react-hook-form';
|
||||||
|
|
||||||
|
import CompaniesTypeahead from '~/components/shared/CompaniesTypeahead';
|
||||||
|
|
||||||
|
type Props = Omit<
|
||||||
|
ComponentProps<typeof CompaniesTypeahead>,
|
||||||
|
'onSelect' | 'value'
|
||||||
|
> & {
|
||||||
|
names: { label: string; value: string };
|
||||||
|
};
|
||||||
|
|
||||||
|
export default function FormCompaniesTypeahead({ names, ...props }: Props) {
|
||||||
|
const { setValue } = useFormContext();
|
||||||
|
const watchCompanyId = useWatch({
|
||||||
|
name: names.value,
|
||||||
|
});
|
||||||
|
const watchCompanyName = useWatch({
|
||||||
|
name: names.label,
|
||||||
|
});
|
||||||
|
|
||||||
|
return (
|
||||||
|
<CompaniesTypeahead
|
||||||
|
{...props}
|
||||||
|
value={{
|
||||||
|
id: watchCompanyId,
|
||||||
|
label: watchCompanyName,
|
||||||
|
value: watchCompanyId,
|
||||||
|
}}
|
||||||
|
onSelect={(option) => {
|
||||||
|
setValue(names.value, option?.value);
|
||||||
|
setValue(names.label, option?.value);
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
}
|
||||||
@@ -0,0 +1,34 @@
|
|||||||
|
import type { ComponentProps } from 'react';
|
||||||
|
import { useFormContext, useWatch } from 'react-hook-form';
|
||||||
|
|
||||||
|
import type { JobTitleType } from '~/components/shared/JobTitles';
|
||||||
|
import { getLabelForJobTitleType } from '~/components/shared/JobTitles';
|
||||||
|
import JobTitlesTypeahead from '~/components/shared/JobTitlesTypahead';
|
||||||
|
|
||||||
|
type Props = Omit<
|
||||||
|
ComponentProps<typeof JobTitlesTypeahead>,
|
||||||
|
'onSelect' | 'value'
|
||||||
|
> & {
|
||||||
|
name: string;
|
||||||
|
};
|
||||||
|
|
||||||
|
export default function FormJobTitlesTypeahead({ name, ...props }: Props) {
|
||||||
|
const { setValue } = useFormContext();
|
||||||
|
const watchJobTitle = useWatch({
|
||||||
|
name,
|
||||||
|
});
|
||||||
|
|
||||||
|
return (
|
||||||
|
<JobTitlesTypeahead
|
||||||
|
{...props}
|
||||||
|
value={{
|
||||||
|
id: watchJobTitle,
|
||||||
|
label: getLabelForJobTitleType(watchJobTitle as JobTitleType),
|
||||||
|
value: watchJobTitle,
|
||||||
|
}}
|
||||||
|
onSelect={(option) => {
|
||||||
|
setValue(name, option?.value);
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
}
|
||||||
@@ -33,13 +33,13 @@ const defaultOfferValues = {
|
|||||||
cityId: '',
|
cityId: '',
|
||||||
comments: '',
|
comments: '',
|
||||||
companyId: '',
|
companyId: '',
|
||||||
jobTitle: '',
|
|
||||||
jobType: JobType.FULLTIME,
|
jobType: JobType.FULLTIME,
|
||||||
monthYearReceived: {
|
monthYearReceived: {
|
||||||
month: getCurrentMonth() as Month,
|
month: getCurrentMonth() as Month,
|
||||||
year: getCurrentYear(),
|
year: getCurrentYear(),
|
||||||
},
|
},
|
||||||
negotiationStrategy: '',
|
negotiationStrategy: '',
|
||||||
|
title: '',
|
||||||
};
|
};
|
||||||
|
|
||||||
export const defaultFullTimeOfferValues = {
|
export const defaultFullTimeOfferValues = {
|
||||||
@@ -108,6 +108,7 @@ export default function OffersSubmissionForm({
|
|||||||
const pageRef = useRef<HTMLDivElement>(null);
|
const pageRef = useRef<HTMLDivElement>(null);
|
||||||
const scrollToTop = () =>
|
const scrollToTop = () =>
|
||||||
pageRef.current?.scrollTo({ behavior: 'smooth', top: 0 });
|
pageRef.current?.scrollTo({ behavior: 'smooth', top: 0 });
|
||||||
|
|
||||||
const formMethods = useForm<OffersProfileFormData>({
|
const formMethods = useForm<OffersProfileFormData>({
|
||||||
defaultValues: initialOfferProfileValues,
|
defaultValues: initialOfferProfileValues,
|
||||||
mode: 'all',
|
mode: 'all',
|
||||||
|
|||||||
@@ -4,11 +4,6 @@ import { Collapsible, RadioList } from '@tih/ui';
|
|||||||
|
|
||||||
import { FieldError } from '~/components/offers/constants';
|
import { FieldError } from '~/components/offers/constants';
|
||||||
import type { BackgroundPostData } from '~/components/offers/types';
|
import type { BackgroundPostData } from '~/components/offers/types';
|
||||||
import CitiesTypeahead from '~/components/shared/CitiesTypeahead';
|
|
||||||
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 {
|
import {
|
||||||
Currency,
|
Currency,
|
||||||
@@ -17,6 +12,9 @@ import {
|
|||||||
|
|
||||||
import { EducationFieldOptions } from '../../EducationFields';
|
import { EducationFieldOptions } from '../../EducationFields';
|
||||||
import { EducationLevelOptions } from '../../EducationLevels';
|
import { EducationLevelOptions } from '../../EducationLevels';
|
||||||
|
import FormCitiesTypeahead from '../../forms/FormCitiesTypeahead';
|
||||||
|
import FormCompaniesTypeahead from '../../forms/FormCompaniesTypeahead';
|
||||||
|
import FormJobTitlesTypeahead from '../../forms/FormJobTitlesTypeahead';
|
||||||
import FormRadioList from '../../forms/FormRadioList';
|
import FormRadioList from '../../forms/FormRadioList';
|
||||||
import FormSection from '../../forms/FormSection';
|
import FormSection from '../../forms/FormSection';
|
||||||
import FormSelect from '../../forms/FormSelect';
|
import FormSelect from '../../forms/FormSelect';
|
||||||
@@ -85,56 +83,19 @@ function YoeSection() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function FullTimeJobFields() {
|
function FullTimeJobFields() {
|
||||||
const { register, setValue, formState } = useFormContext<{
|
const { register, formState } = useFormContext<{
|
||||||
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',
|
|
||||||
});
|
|
||||||
const watchCityId = useWatch({
|
|
||||||
name: 'background.experiences.0.cityId',
|
|
||||||
});
|
|
||||||
const watchCityName = useWatch({
|
|
||||||
name: 'background.experiences.0.cityName',
|
|
||||||
});
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<div className="grid grid-cols-1 gap-y-4 sm:grid-cols-2 sm:gap-6">
|
<div className="grid grid-cols-1 gap-y-4 sm:grid-cols-2 sm:gap-6">
|
||||||
<JobTitlesTypeahead
|
<FormJobTitlesTypeahead name="background.experiences.0.title" />
|
||||||
value={{
|
<FormCompaniesTypeahead
|
||||||
id: watchJobTitle,
|
names={{
|
||||||
label: getLabelForJobTitleType(watchJobTitle as JobTitleType),
|
label: 'background.experiences.0.companyName',
|
||||||
value: watchJobTitle,
|
value: 'background.experiences.0.companyId',
|
||||||
}}
|
|
||||||
onSelect={(option) => {
|
|
||||||
if (option) {
|
|
||||||
setValue('background.experiences.0.title', option.value);
|
|
||||||
}
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
<CompaniesTypeahead
|
|
||||||
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);
|
|
||||||
} else {
|
|
||||||
setValue('background.experiences.0.companyId', '');
|
|
||||||
setValue('background.experiences.0.companyName', '');
|
|
||||||
}
|
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
@@ -172,21 +133,10 @@ function FullTimeJobFields() {
|
|||||||
placeholder="e.g. L4, Junior"
|
placeholder="e.g. L4, Junior"
|
||||||
{...register(`background.experiences.0.level`)}
|
{...register(`background.experiences.0.level`)}
|
||||||
/>
|
/>
|
||||||
<CitiesTypeahead
|
<FormCitiesTypeahead
|
||||||
label="Location"
|
names={{
|
||||||
value={{
|
label: 'background.experiences.0.cityName',
|
||||||
id: watchCityId,
|
value: 'background.experiences.0.cityId',
|
||||||
label: watchCityName,
|
|
||||||
value: watchCityId,
|
|
||||||
}}
|
|
||||||
onSelect={(option) => {
|
|
||||||
if (option) {
|
|
||||||
setValue('background.experiences.0.cityId', option.value);
|
|
||||||
setValue('background.experiences.0.cityName', option.label);
|
|
||||||
} else {
|
|
||||||
setValue('background.experiences.0.cityId', '');
|
|
||||||
setValue('background.experiences.0.cityName', '');
|
|
||||||
}
|
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
<FormTextInput
|
<FormTextInput
|
||||||
@@ -205,53 +155,19 @@ function FullTimeJobFields() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function InternshipJobFields() {
|
function InternshipJobFields() {
|
||||||
const { register, setValue, formState } = useFormContext<{
|
const { register, formState } = useFormContext<{
|
||||||
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',
|
|
||||||
});
|
|
||||||
const watchCityId = useWatch({
|
|
||||||
name: 'background.experiences.0.cityId',
|
|
||||||
});
|
|
||||||
const watchCityName = useWatch({
|
|
||||||
name: 'background.experiences.0.cityName',
|
|
||||||
});
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<div className="grid grid-cols-1 gap-y-4 sm:grid-cols-2 sm:gap-6">
|
<div className="grid grid-cols-1 gap-y-4 sm:grid-cols-2 sm:gap-6">
|
||||||
<JobTitlesTypeahead
|
<FormJobTitlesTypeahead name="background.experiences.0.title" />
|
||||||
value={{
|
<FormCompaniesTypeahead
|
||||||
id: watchJobTitle,
|
names={{
|
||||||
label: getLabelForJobTitleType(watchJobTitle as JobTitleType),
|
label: 'background.experiences.0.companyName',
|
||||||
value: watchJobTitle,
|
value: 'background.experiences.0.companyId',
|
||||||
}}
|
|
||||||
onSelect={(option) => {
|
|
||||||
if (option) {
|
|
||||||
setValue('background.experiences.0.title', option.value);
|
|
||||||
}
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
<CompaniesTypeahead
|
|
||||||
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>
|
||||||
@@ -280,21 +196,10 @@ function InternshipJobFields() {
|
|||||||
/>
|
/>
|
||||||
<Collapsible label="Add more details">
|
<Collapsible label="Add more details">
|
||||||
<div className="grid grid-cols-1 gap-6 sm:grid-cols-2">
|
<div className="grid grid-cols-1 gap-6 sm:grid-cols-2">
|
||||||
<CitiesTypeahead
|
<FormCitiesTypeahead
|
||||||
label="Location"
|
names={{
|
||||||
value={{
|
label: 'background.experiences.0.cityName',
|
||||||
id: watchCityId,
|
value: 'background.experiences.0.cityId',
|
||||||
label: watchCityName,
|
|
||||||
value: watchCityId,
|
|
||||||
}}
|
|
||||||
onSelect={(option) => {
|
|
||||||
if (option) {
|
|
||||||
setValue('background.experiences.0.cityId', option.value);
|
|
||||||
setValue('background.experiences.0.cityName', option.label);
|
|
||||||
} else {
|
|
||||||
setValue('background.experiences.0.cityId', '');
|
|
||||||
setValue('background.experiences.0.cityName', '');
|
|
||||||
}
|
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
<FormTextInput
|
<FormTextInput
|
||||||
@@ -388,4 +293,4 @@ export default function BackgroundForm() {
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -4,6 +4,7 @@ import type {
|
|||||||
UseFieldArrayRemove,
|
UseFieldArrayRemove,
|
||||||
UseFieldArrayReturn,
|
UseFieldArrayReturn,
|
||||||
} from 'react-hook-form';
|
} from 'react-hook-form';
|
||||||
|
import { Controller } from 'react-hook-form';
|
||||||
import { useWatch } from 'react-hook-form';
|
import { useWatch } from 'react-hook-form';
|
||||||
import { useFormContext } from 'react-hook-form';
|
import { useFormContext } from 'react-hook-form';
|
||||||
import { useFieldArray } from 'react-hook-form';
|
import { useFieldArray } from 'react-hook-form';
|
||||||
@@ -12,17 +13,14 @@ import { TrashIcon } from '@heroicons/react/24/outline';
|
|||||||
import { JobType } from '@prisma/client';
|
import { JobType } from '@prisma/client';
|
||||||
import { Button, Dialog, HorizontalDivider } from '@tih/ui';
|
import { Button, Dialog, HorizontalDivider } from '@tih/ui';
|
||||||
|
|
||||||
import CitiesTypeahead from '~/components/shared/CitiesTypeahead';
|
|
||||||
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 {
|
import {
|
||||||
defaultFullTimeOfferValues,
|
defaultFullTimeOfferValues,
|
||||||
defaultInternshipOfferValues,
|
defaultInternshipOfferValues,
|
||||||
} from '../OffersSubmissionForm';
|
} from '../OffersSubmissionForm';
|
||||||
import { FieldError, JobTypeLabel } from '../../constants';
|
import { FieldError, JobTypeLabel } from '../../constants';
|
||||||
|
import FormCitiesTypeahead from '../../forms/FormCitiesTypeahead';
|
||||||
|
import FormCompaniesTypeahead from '../../forms/FormCompaniesTypeahead';
|
||||||
|
import FormJobTitlesTypeahead from '../../forms/FormJobTitlesTypeahead';
|
||||||
import FormMonthYearPicker from '../../forms/FormMonthYearPicker';
|
import FormMonthYearPicker from '../../forms/FormMonthYearPicker';
|
||||||
import FormSection from '../../forms/FormSection';
|
import FormSection from '../../forms/FormSection';
|
||||||
import FormSelect from '../../forms/FormSelect';
|
import FormSelect from '../../forms/FormSelect';
|
||||||
@@ -46,26 +44,11 @@ function FullTimeOfferDetailsForm({
|
|||||||
index,
|
index,
|
||||||
remove,
|
remove,
|
||||||
}: FullTimeOfferDetailsFormProps) {
|
}: FullTimeOfferDetailsFormProps) {
|
||||||
const { register, formState, setValue } = useFormContext<{
|
const { register, formState, setValue, control } = 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}.offersFullTime.title`,
|
|
||||||
});
|
|
||||||
const watchCompanyId = useWatch({
|
|
||||||
name: `offers.${index}.companyId`,
|
|
||||||
});
|
|
||||||
const watchCompanyName = useWatch({
|
|
||||||
name: `offers.${index}.companyName`,
|
|
||||||
});
|
|
||||||
const watchCityId = useWatch({
|
|
||||||
name: `offers.${index}.cityId`,
|
|
||||||
});
|
|
||||||
const watchCityName = useWatch({
|
|
||||||
name: `offers.${index}.cityName`,
|
|
||||||
});
|
|
||||||
const watchCurrency = useWatch({
|
const watchCurrency = useWatch({
|
||||||
name: `offers.${index}.offersFullTime.totalCompensation.currency`,
|
name: `offers.${index}.offersFullTime.totalCompensation.currency`,
|
||||||
});
|
});
|
||||||
@@ -83,18 +66,17 @@ function FullTimeOfferDetailsForm({
|
|||||||
<div className="space-y-8 rounded-lg border border-slate-200 p-6 sm:space-y-16 sm:p-8">
|
<div className="space-y-8 rounded-lg border border-slate-200 p-6 sm:space-y-16 sm:p-8">
|
||||||
<FormSection title="Company & Title Information">
|
<FormSection title="Company & Title Information">
|
||||||
<div className="grid grid-cols-1 gap-y-4 sm:grid-cols-2 sm:gap-6">
|
<div className="grid grid-cols-1 gap-y-4 sm:grid-cols-2 sm:gap-6">
|
||||||
<JobTitlesTypeahead
|
<Controller
|
||||||
required={true}
|
control={control}
|
||||||
value={{
|
name={`offers.${index}.offersFullTime.title`}
|
||||||
id: watchJobTitle,
|
render={() => (
|
||||||
label: getLabelForJobTitleType(watchJobTitle as JobTitleType),
|
<FormJobTitlesTypeahead
|
||||||
value: watchJobTitle,
|
errorMessage={offerFields?.offersFullTime?.title?.message}
|
||||||
}}
|
name={`offers.${index}.offersFullTime.title`}
|
||||||
onSelect={(option) => {
|
required={true}
|
||||||
if (option) {
|
/>
|
||||||
setValue(`offers.${index}.offersFullTime.title`, option.value);
|
)}
|
||||||
}
|
rules={{ required: true }}
|
||||||
}}
|
|
||||||
/>
|
/>
|
||||||
<FormTextInput
|
<FormTextInput
|
||||||
errorMessage={offerFields?.offersFullTime?.level?.message}
|
errorMessage={offerFields?.offersFullTime?.level?.message}
|
||||||
@@ -107,37 +89,35 @@ function FullTimeOfferDetailsForm({
|
|||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
<div className="grid grid-cols-1 gap-y-4 sm:grid-cols-2 sm:gap-6">
|
<div className="grid grid-cols-1 gap-y-4 sm:grid-cols-2 sm:gap-6">
|
||||||
<CompaniesTypeahead
|
<Controller
|
||||||
required={true}
|
control={control}
|
||||||
value={{
|
name={`offers.${index}.companyId`}
|
||||||
id: watchCompanyId,
|
render={() => (
|
||||||
label: watchCompanyName,
|
<FormCompaniesTypeahead
|
||||||
value: watchCompanyId,
|
errorMessage={offerFields?.companyId?.message}
|
||||||
}}
|
names={{
|
||||||
onSelect={(option) => {
|
label: `offers.${index}.companyName`,
|
||||||
if (option) {
|
value: `offers.${index}.companyId`,
|
||||||
setValue(`offers.${index}.companyId`, option.value);
|
}}
|
||||||
setValue(`offers.${index}.companyName`, option.label);
|
required={true}
|
||||||
}
|
/>
|
||||||
}}
|
)}
|
||||||
|
rules={{ required: true }}
|
||||||
/>
|
/>
|
||||||
<CitiesTypeahead
|
<Controller
|
||||||
label="Location"
|
control={control}
|
||||||
required={true}
|
name={`offers.${index}.cityId`}
|
||||||
value={{
|
render={() => (
|
||||||
id: watchCityId,
|
<FormCitiesTypeahead
|
||||||
label: watchCityName,
|
errorMessage={offerFields?.cityId?.message}
|
||||||
value: watchCityId,
|
names={{
|
||||||
}}
|
label: `offers.${index}.cityName`,
|
||||||
onSelect={(option) => {
|
value: `offers.${index}.companyId`,
|
||||||
if (option) {
|
}}
|
||||||
setValue(`offers.${index}.cityId`, option.value);
|
required={true}
|
||||||
setValue(`offers.${index}.cityName`, option.label);
|
/>
|
||||||
} else {
|
)}
|
||||||
setValue(`offers.${index}.cityId`, '');
|
rules={{ required: true }}
|
||||||
setValue(`offers.${index}.cityName`, '');
|
|
||||||
}
|
|
||||||
}}
|
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
</FormSection>
|
</FormSection>
|
||||||
@@ -303,76 +283,56 @@ function InternshipOfferDetailsForm({
|
|||||||
index,
|
index,
|
||||||
remove,
|
remove,
|
||||||
}: InternshipOfferDetailsFormProps) {
|
}: InternshipOfferDetailsFormProps) {
|
||||||
const { register, formState, setValue } = useFormContext<{
|
const { register, formState, control } = 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`,
|
|
||||||
});
|
|
||||||
const watchCityId = useWatch({
|
|
||||||
name: `offers.${index}.cityId`,
|
|
||||||
});
|
|
||||||
const watchCityName = useWatch({
|
|
||||||
name: `offers.${index}.cityName`,
|
|
||||||
});
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="space-y-8 rounded-lg border border-slate-200 p-6 sm:space-y-16 sm:p-8">
|
<div className="space-y-8 rounded-lg border border-slate-200 p-6 sm:space-y-16 sm:p-8">
|
||||||
<FormSection title="Company & Title Information">
|
<FormSection title="Company & Title Information">
|
||||||
<JobTitlesTypeahead
|
<Controller
|
||||||
required={true}
|
control={control}
|
||||||
value={{
|
name={`offers.${index}.offersIntern.title`}
|
||||||
id: watchJobTitle,
|
render={() => (
|
||||||
label: getLabelForJobTitleType(watchJobTitle as JobTitleType),
|
<FormJobTitlesTypeahead
|
||||||
value: watchJobTitle,
|
errorMessage={offerFields?.offersIntern?.title?.message}
|
||||||
}}
|
name={`offers.${index}.offersIntern.title`}
|
||||||
onSelect={(option) => {
|
required={true}
|
||||||
if (option) {
|
/>
|
||||||
setValue(`offers.${index}.offersIntern.title`, option.value);
|
)}
|
||||||
}
|
rules={{ required: true }}
|
||||||
}}
|
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<div className="grid grid-cols-1 gap-y-4 sm:grid-cols-2 sm:gap-6">
|
<div className="grid grid-cols-1 gap-y-4 sm:grid-cols-2 sm:gap-6">
|
||||||
<CompaniesTypeahead
|
<Controller
|
||||||
required={true}
|
control={control}
|
||||||
value={{
|
name={`offers.${index}.companyId`}
|
||||||
id: watchCompanyId,
|
render={() => (
|
||||||
label: watchCompanyName,
|
<FormCompaniesTypeahead
|
||||||
value: watchCompanyId,
|
errorMessage={offerFields?.companyId?.message}
|
||||||
}}
|
names={{
|
||||||
onSelect={(option) => {
|
label: `offers.${index}.companyName`,
|
||||||
if (option) {
|
value: `offers.${index}.companyId`,
|
||||||
setValue(`offers.${index}.companyId`, option.value);
|
}}
|
||||||
setValue(`offers.${index}.companyName`, option.label);
|
required={true}
|
||||||
}
|
/>
|
||||||
}}
|
)}
|
||||||
|
rules={{ required: true }}
|
||||||
/>
|
/>
|
||||||
<CitiesTypeahead
|
<Controller
|
||||||
label="Location"
|
control={control}
|
||||||
required={true}
|
name={`offers.${index}.cityId`}
|
||||||
value={{
|
render={() => (
|
||||||
id: watchCityId,
|
<FormCitiesTypeahead
|
||||||
label: watchCityName,
|
errorMessage={offerFields?.cityId?.message}
|
||||||
value: watchCityId,
|
names={{
|
||||||
}}
|
label: `offers.${index}.cityName`,
|
||||||
onSelect={(option) => {
|
value: `offers.${index}.companyId`,
|
||||||
if (option) {
|
}}
|
||||||
setValue(`offers.${index}.cityId`, option.value);
|
required={true}
|
||||||
setValue(`offers.${index}.cityName`, option.label);
|
/>
|
||||||
} else {
|
)}
|
||||||
setValue(`offers.${index}.cityId`, '');
|
rules={{ required: true }}
|
||||||
setValue(`offers.${index}.cityName`, '');
|
|
||||||
}
|
|
||||||
}}
|
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
<div className="grid grid-cols-1 gap-y-4 sm:grid-cols-2 sm:gap-6">
|
<div className="grid grid-cols-1 gap-y-4 sm:grid-cols-2 sm:gap-6">
|
||||||
|
|||||||
Reference in New Issue
Block a user