Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
40 commits
Select commit Hold shift + click to select a range
b4a066c
refactor(onboarding-invite) - use new mutation method
gabrielseco Feb 25, 2026
74e71a0
fix tests
gabrielseco Feb 25, 2026
7932663
refetch employment
gabrielseco Feb 25, 2026
a42a4d2
fix(contractor-onboarding) - fetch the contract details only when we …
gabrielseco Feb 25, 2026
a2c27ef
fix review
gabrielseco Feb 25, 2026
b39b461
Merge branch 'fix-contract-details-stale-fetch' into pbyr-3518-build-…
gabrielseco Feb 25, 2026
673e720
fix error
gabrielseco Feb 25, 2026
fe65640
Merge branch 'main' into pbyr-3518-build-credit-risk-flow-for-cor
gabrielseco Feb 27, 2026
cb3270c
Merge branch 'refactor-onboarding-invite-before-reserve-invoice' into…
gabrielseco Feb 27, 2026
7724a5d
Merge branch 'main' into pbyr-3518-build-credit-risk-flow-for-cor
gabrielseco Mar 4, 2026
836bc0a
Merge branch 'main' into pbyr-3518-build-credit-risk-flow-for-cor
gabrielseco Mar 6, 2026
94df1a4
Merge branch 'main' into pbyr-3518-build-credit-risk-flow-for-cor
gabrielseco Mar 6, 2026
f8ba026
fix conflicts
gabrielseco Mar 6, 2026
726a81d
fix(contrator-onboarding) - create intent when eligibility was answer…
gabrielseco Mar 6, 2026
e98945c
fix file
gabrielseco Mar 6, 2026
7ee0925
fix
gabrielseco Mar 6, 2026
4d92470
Merge branch 'fix-cor-intent' into pbyr-3518-build-credit-risk-flow-f…
gabrielseco Mar 6, 2026
e0a7350
add logic to create reserve
gabrielseco Mar 6, 2026
9da2519
fix review statuses
gabrielseco Mar 6, 2026
3641421
add test
gabrielseco Mar 6, 2026
4d3d1bc
fix tests
gabrielseco Mar 6, 2026
5106426
fix tests
gabrielseco Mar 6, 2026
cc593bd
Merge branch 'main' into pbyr-3518-build-credit-risk-flow-for-cor
gabrielseco Mar 6, 2026
5ac1576
Merge branch 'main' into pbyr-3518-build-credit-risk-flow-for-cor
gabrielseco Mar 11, 2026
be648c3
fix utils
gabrielseco Mar 11, 2026
80271b8
fix types
gabrielseco Mar 12, 2026
0432fd2
fix test
gabrielseco Mar 12, 2026
6ced4e7
let people invite
gabrielseco Mar 13, 2026
88e5239
fix who can invite
gabrielseco Mar 13, 2026
2006d01
fix ifs
gabrielseco Mar 13, 2026
3eb47d5
Merge branch 'main' into pbyr-3518-build-credit-risk-flow-for-cor
gabrielseco Mar 13, 2026
e14b02f
refactor(onboarding-invite) - move business logic internally
gabrielseco Mar 13, 2026
1802bd8
add tests
gabrielseco Mar 13, 2026
b9a2ca4
Merge branch 'move-can-invite-internally' into pbyr-3518-build-credit…
gabrielseco Mar 13, 2026
ad860d3
fix tests
gabrielseco Mar 13, 2026
c71053e
fix types
gabrielseco Mar 13, 2026
c032294
align
gabrielseco Mar 13, 2026
497b386
Merge branch 'main' into pbyr-3518-build-credit-risk-flow-for-cor
gabrielseco Mar 16, 2026
1a205b9
change to status
gabrielseco Mar 16, 2026
f875626
fix tests
gabrielseco Mar 16, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 4 additions & 2 deletions example/src/ReviewContractorOnboardingStep.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -156,8 +156,10 @@ export const ReviewContractorOnboardingStep = ({
</BackButton>
<OnboardingInvite
className='submit-button'
render={() => {
return 'Invite Contractor';
render={({ status }) => {
return status === 'create_reserve'
? 'Create Reserve'
: 'Invite Contractor';
}}
onSuccess={() => {
console.log('Contractor invited');
Expand Down
45 changes: 35 additions & 10 deletions src/flows/ContractorOnboarding/components/OnboardingInvite.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,9 @@
import { ButtonHTMLAttributes, ReactNode } from 'react';
import omit from 'lodash.omit';
import { useEmploymentInvite } from '@/src/flows/Onboarding/api';
import {
useCreateReserveInvoice,
useEmploymentInvite,
} from '@/src/flows/Onboarding/api';
import { FieldError, mutationToPromise } from '@/src/lib/mutations';
import { SuccessResponse } from '@/src/client';
import { useFormFields } from '@/src/context';
Expand All @@ -16,7 +19,7 @@ export type OnboardingInviteProps = Omit<
employmentStatus,
}: {
data: SuccessResponse;
employmentStatus: 'invited';
employmentStatus: 'invited' | 'created_awaiting_reserve';
}) => void | Promise<void>;
onError?: ({
error,
Expand All @@ -28,7 +31,7 @@ export type OnboardingInviteProps = Omit<
fieldErrors: FieldError[];
}) => void;
onSubmit?: () => void | Promise<void>;
render: (props: { employmentStatus: 'invited' }) => ReactNode;
render: (props: { status: 'invite' | 'create_reserve' }) => ReactNode;
} & Record<string, unknown>;

export function OnboardingInvite({
Expand All @@ -41,20 +44,42 @@ export function OnboardingInvite({
const { components } = useFormFields();
const { contractorOnboardingBag } = useContractorOnboardingContext();
const employmentInviteMutation = useEmploymentInvite();
const createReserveInvoiceMutation = useCreateReserveInvoice();

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

const { mutateAsyncOrThrow: createReserveInvoiceMutationAsync } =
mutationToPromise(createReserveInvoiceMutation);

const isCOR = contractorOnboardingBag.employment?.contractor_type === 'cor';

const isReserveFlow = isCOR && !contractorOnboardingBag.isEmploymentReadOnly;

const handleSubmit = async () => {
try {
await onSubmit?.();
if (contractorOnboardingBag.employmentId) {

if (isReserveFlow && contractorOnboardingBag.employmentId) {
const response = await createReserveInvoiceMutationAsync({
employment_slug: contractorOnboardingBag.employmentId as string,
});
if (response?.data) {
await onSuccess?.({
data: response,
employmentStatus: 'created_awaiting_reserve',
});

contractorOnboardingBag.refetchEmployment();
return;
}
Comment thread
cursor[bot] marked this conversation as resolved.
} else if (contractorOnboardingBag.employmentId) {
const data = await employmentInviteMutationAsync({
employment_id: contractorOnboardingBag.employmentId,
});
if (data) {
await onSuccess?.({
data: data as SuccessResponse,
data: data,
employmentStatus: 'invited',
});
contractorOnboardingBag.refetchEmployment();
Expand All @@ -78,11 +103,11 @@ export function OnboardingInvite({
throw new Error(`Button component not found`);
}

const disabled = Boolean(
const disabled =
employmentInviteMutation.isPending ||
!contractorOnboardingBag.canInvite ||
props.disabled,
);
createReserveInvoiceMutation.isPending ||
!contractorOnboardingBag.canInvite ||
props.disabled;
Comment thread
cursor[bot] marked this conversation as resolved.

return (
<CustomButton
Expand All @@ -94,7 +119,7 @@ export function OnboardingInvite({
}}
>
{render({
employmentStatus: 'invited',
status: isReserveFlow ? 'create_reserve' : 'invite',
})}
</CustomButton>
);
Expand Down
7 changes: 4 additions & 3 deletions src/flows/ContractorOnboarding/hooks.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -384,8 +384,8 @@ export const useContractorOnboarding = ({
);

const { data: contractDocuments, isLoading: isLoadingContractDocuments } =
useGetContractDocuments(employmentId as string, {
enabled: Boolean(employmentId),
useGetContractDocuments(internalEmploymentId as string, {
enabled: Boolean(internalEmploymentId),
});

useEffect(() => {
Expand Down Expand Up @@ -1034,13 +1034,14 @@ export const useContractorOnboarding = ({
}

case 'contract_preview': {
return signContractDocumentMutationAsync({
const response = await signContractDocumentMutationAsync({
employmentId: internalEmploymentId as string,
contractDocumentId: internalContractDocumentId as string,
payload: {
signature: parsedValues.signature,
},
});
return response;
}
case 'pricing_plan': {
const blockedProductsEligibility = [
Expand Down
17 changes: 17 additions & 0 deletions src/flows/ContractorOnboarding/tests/ContractorOnboarding.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,7 @@ import {
mockCMOnlyResponse,
} from '@/src/common/api/fixtures/contractors-subscriptions';
import { mockBlockedEligibilityQuestionnaireResponse } from '@/src/common/api/fixtures/eligibility-questionnaire';
import { mockContractorBasicInformationSchema } from '@/src/common/api/fixtures/contractors';

const mockOnSubmit = vi.fn();
const mockOnSuccess = vi.fn();
Expand Down Expand Up @@ -383,6 +384,9 @@ describe('ContractorOnboardingFlow', () => {
mockRender.mockReset();

server.use(
http.get('*/v1/countries/*/employment_basic_information*', () => {
return HttpResponse.json(mockContractorBasicInformationSchema);
}),
http.get('*/v1/employments/:id', ({ params }) => {
const employmentId = params?.id;

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

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

await fillContractorSubscription();

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

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

await fillContractorSubscription();

await waitFor(() => {
Expand Down Expand Up @@ -686,6 +693,7 @@ describe('ContractorOnboardingFlow', () => {
nextButton.click();

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

await fillContractorSubscription();

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

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

await fillContractorSubscription();

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

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

await fillContractorSubscription();

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

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

await fillContractorSubscription();

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

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

await fillContractorSubscription();

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

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

await fillContractorSubscription('Contractor of Record');

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

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

await waitForElementToBeRemoved(() => screen.getByTestId('spinner'));

await fillContractorSubscription('Contractor of Record');

nextButton = screen.getByText(/Next Step/i);
Expand Down
Loading
Loading