Skip to content

Commit 31539c2

Browse files
authored
feat(credit-risk) - create reserves when COR is selected (#760)
* refactor(onboarding-invite) - use new mutation method * fix tests * refetch employment * fix(contractor-onboarding) - fetch the contract details only when we arrive at the step * fix review * fix error * fix conflicts * fix(contrator-onboarding) - create intent when eligibility was answered in the past * fix file * fix * add logic to create reserve * fix review statuses * add test * fix tests * fix tests * fix utils * fix types * fix test * let people invite * fix who can invite * fix ifs * refactor(onboarding-invite) - move business logic internally * add tests * fix tests * fix types * align * change to status * fix tests
1 parent 96b76cd commit 31539c2

6 files changed

Lines changed: 317 additions & 43 deletions

File tree

example/src/ReviewContractorOnboardingStep.tsx

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -156,8 +156,10 @@ export const ReviewContractorOnboardingStep = ({
156156
</BackButton>
157157
<OnboardingInvite
158158
className='submit-button'
159-
render={() => {
160-
return 'Invite Contractor';
159+
render={({ status }) => {
160+
return status === 'create_reserve'
161+
? 'Create Reserve'
162+
: 'Invite Contractor';
161163
}}
162164
onSuccess={() => {
163165
console.log('Contractor invited');

src/flows/ContractorOnboarding/components/OnboardingInvite.tsx

Lines changed: 35 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,9 @@
11
import { ButtonHTMLAttributes, ReactNode } from 'react';
22
import omit from 'lodash.omit';
3-
import { useEmploymentInvite } from '@/src/flows/Onboarding/api';
3+
import {
4+
useCreateReserveInvoice,
5+
useEmploymentInvite,
6+
} from '@/src/flows/Onboarding/api';
47
import { FieldError, mutationToPromise } from '@/src/lib/mutations';
58
import { SuccessResponse } from '@/src/client';
69
import { useFormFields } from '@/src/context';
@@ -16,7 +19,7 @@ export type OnboardingInviteProps = Omit<
1619
employmentStatus,
1720
}: {
1821
data: SuccessResponse;
19-
employmentStatus: 'invited';
22+
employmentStatus: 'invited' | 'created_awaiting_reserve';
2023
}) => void | Promise<void>;
2124
onError?: ({
2225
error,
@@ -28,7 +31,7 @@ export type OnboardingInviteProps = Omit<
2831
fieldErrors: FieldError[];
2932
}) => void;
3033
onSubmit?: () => void | Promise<void>;
31-
render: (props: { employmentStatus: 'invited' }) => ReactNode;
34+
render: (props: { status: 'invite' | 'create_reserve' }) => ReactNode;
3235
} & Record<string, unknown>;
3336

3437
export function OnboardingInvite({
@@ -41,20 +44,42 @@ export function OnboardingInvite({
4144
const { components } = useFormFields();
4245
const { contractorOnboardingBag } = useContractorOnboardingContext();
4346
const employmentInviteMutation = useEmploymentInvite();
47+
const createReserveInvoiceMutation = useCreateReserveInvoice();
4448

4549
const { mutateAsyncOrThrow: employmentInviteMutationAsync } =
4650
mutationToPromise(employmentInviteMutation);
4751

52+
const { mutateAsyncOrThrow: createReserveInvoiceMutationAsync } =
53+
mutationToPromise(createReserveInvoiceMutation);
54+
55+
const isCOR = contractorOnboardingBag.employment?.contractor_type === 'cor';
56+
57+
const isReserveFlow = isCOR && !contractorOnboardingBag.isEmploymentReadOnly;
58+
4859
const handleSubmit = async () => {
4960
try {
5061
await onSubmit?.();
51-
if (contractorOnboardingBag.employmentId) {
62+
63+
if (isReserveFlow && contractorOnboardingBag.employmentId) {
64+
const response = await createReserveInvoiceMutationAsync({
65+
employment_slug: contractorOnboardingBag.employmentId as string,
66+
});
67+
if (response?.data) {
68+
await onSuccess?.({
69+
data: response,
70+
employmentStatus: 'created_awaiting_reserve',
71+
});
72+
73+
contractorOnboardingBag.refetchEmployment();
74+
return;
75+
}
76+
} else if (contractorOnboardingBag.employmentId) {
5277
const data = await employmentInviteMutationAsync({
5378
employment_id: contractorOnboardingBag.employmentId,
5479
});
5580
if (data) {
5681
await onSuccess?.({
57-
data: data as SuccessResponse,
82+
data: data,
5883
employmentStatus: 'invited',
5984
});
6085
contractorOnboardingBag.refetchEmployment();
@@ -78,11 +103,11 @@ export function OnboardingInvite({
78103
throw new Error(`Button component not found`);
79104
}
80105

81-
const disabled = Boolean(
106+
const disabled =
82107
employmentInviteMutation.isPending ||
83-
!contractorOnboardingBag.canInvite ||
84-
props.disabled,
85-
);
108+
createReserveInvoiceMutation.isPending ||
109+
!contractorOnboardingBag.canInvite ||
110+
props.disabled;
86111

87112
return (
88113
<CustomButton
@@ -94,7 +119,7 @@ export function OnboardingInvite({
94119
}}
95120
>
96121
{render({
97-
employmentStatus: 'invited',
122+
status: isReserveFlow ? 'create_reserve' : 'invite',
98123
})}
99124
</CustomButton>
100125
);

src/flows/ContractorOnboarding/hooks.tsx

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -384,8 +384,8 @@ export const useContractorOnboarding = ({
384384
);
385385

386386
const { data: contractDocuments, isLoading: isLoadingContractDocuments } =
387-
useGetContractDocuments(employmentId as string, {
388-
enabled: Boolean(employmentId),
387+
useGetContractDocuments(internalEmploymentId as string, {
388+
enabled: Boolean(internalEmploymentId),
389389
});
390390

391391
useEffect(() => {
@@ -1034,13 +1034,14 @@ export const useContractorOnboarding = ({
10341034
}
10351035

10361036
case 'contract_preview': {
1037-
return signContractDocumentMutationAsync({
1037+
const response = await signContractDocumentMutationAsync({
10381038
employmentId: internalEmploymentId as string,
10391039
contractDocumentId: internalContractDocumentId as string,
10401040
payload: {
10411041
signature: parsedValues.signature,
10421042
},
10431043
});
1044+
return response;
10441045
}
10451046
case 'pricing_plan': {
10461047
const blockedProductsEligibility = [

src/flows/ContractorOnboarding/tests/ContractorOnboarding.test.tsx

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,7 @@ import {
4747
mockCMOnlyResponse,
4848
} from '@/src/common/api/fixtures/contractors-subscriptions';
4949
import { mockBlockedEligibilityQuestionnaireResponse } from '@/src/common/api/fixtures/eligibility-questionnaire';
50+
import { mockContractorBasicInformationSchema } from '@/src/common/api/fixtures/contractors';
5051

5152
const mockOnSubmit = vi.fn();
5253
const mockOnSuccess = vi.fn();
@@ -383,6 +384,9 @@ describe('ContractorOnboardingFlow', () => {
383384
mockRender.mockReset();
384385

385386
server.use(
387+
http.get('*/v1/countries/*/employment_basic_information*', () => {
388+
return HttpResponse.json(mockContractorBasicInformationSchema);
389+
}),
386390
http.get('*/v1/employments/:id', ({ params }) => {
387391
const employmentId = params?.id;
388392

@@ -525,6 +529,7 @@ describe('ContractorOnboardingFlow', () => {
525529
nextButton.click();
526530

527531
await screen.findByText(/Step: Pricing Plan/i);
532+
await waitForElementToBeRemoved(() => screen.getByTestId('spinner'));
528533

529534
await fillContractorSubscription();
530535

@@ -571,6 +576,8 @@ describe('ContractorOnboardingFlow', () => {
571576
nextButton.click();
572577

573578
await screen.findByText(/Step: Pricing Plan/i);
579+
await waitForElementToBeRemoved(() => screen.getByTestId('spinner'));
580+
574581
await fillContractorSubscription();
575582

576583
await waitFor(() => {
@@ -686,6 +693,7 @@ describe('ContractorOnboardingFlow', () => {
686693
nextButton.click();
687694

688695
await screen.findByText(/Step: Pricing Plan/i);
696+
await waitForElementToBeRemoved(() => screen.getByTestId('spinner'));
689697

690698
await fillContractorSubscription();
691699

@@ -743,6 +751,7 @@ describe('ContractorOnboardingFlow', () => {
743751
nextButton.click();
744752

745753
await screen.findByText(/Step: Pricing Plan/i);
754+
await waitForElementToBeRemoved(() => screen.getByTestId('spinner'));
746755

747756
await fillContractorSubscription();
748757

@@ -1036,6 +1045,8 @@ describe('ContractorOnboardingFlow', () => {
10361045
nextButton.click();
10371046

10381047
await screen.findByText(/Step: Pricing Plan/i);
1048+
await waitForElementToBeRemoved(() => screen.getByTestId('spinner'));
1049+
10391050
await fillContractorSubscription();
10401051

10411052
nextButton = screen.getByText(/Next Step/i);
@@ -1209,6 +1220,8 @@ describe('ContractorOnboardingFlow', () => {
12091220
nextButton.click();
12101221

12111222
await screen.findByText(/Step: Pricing Plan/i);
1223+
await waitForElementToBeRemoved(() => screen.getByTestId('spinner'));
1224+
12121225
await fillContractorSubscription();
12131226

12141227
nextButton = screen.getByText(/Next Step/i);
@@ -1260,6 +1273,7 @@ describe('ContractorOnboardingFlow', () => {
12601273
nextButton.click();
12611274

12621275
await screen.findByText(/Step: Pricing Plan/i);
1276+
await waitForElementToBeRemoved(() => screen.getByTestId('spinner'));
12631277

12641278
await fillContractorSubscription();
12651279

@@ -1815,6 +1829,7 @@ describe('ContractorOnboardingFlow', () => {
18151829
nextButton.click();
18161830

18171831
await screen.findByText(/Step: Pricing Plan/i);
1832+
await waitForElementToBeRemoved(() => screen.getByTestId('spinner'));
18181833

18191834
await fillContractorSubscription('Contractor of Record');
18201835

@@ -1862,6 +1877,8 @@ describe('ContractorOnboardingFlow', () => {
18621877

18631878
await screen.findByText(/Step: Pricing Plan/i);
18641879

1880+
await waitForElementToBeRemoved(() => screen.getByTestId('spinner'));
1881+
18651882
await fillContractorSubscription('Contractor of Record');
18661883

18671884
nextButton = screen.getByText(/Next Step/i);

0 commit comments

Comments
 (0)