Only show the install-apps onboarding step to workspace creators (#22990)
New users joining an existing workspace through an invite link were routed through the "Install your first apps" onboarding step, which is meant for workspace creation only. #22347 set `ONBOARDING_INSTALL_APPS_PENDING` inside `activateOnboardingForUser`, which is shared by both sign-up paths. Gate it per path like the connect-account step: `true` on `signUpOnNewWorkspace`, `false` on `signInUpOnExistingWorkspace`. Verified locally: invited users now go straight from create-profile into the workspace, and workspace creators still get the install-apps step. Covered by a unit test on the invite path and integration tests asserting the onboarding status per sign-up path (invite → `PROFILE_CREATION`; workspace creation → `SYNC_EMAIL` then `APPS_INSTALLATION`).
This commit is contained in:
+50
-9
@@ -1,3 +1,5 @@
|
||||
import { WorkspaceActivationStatus } from 'twenty-shared/workspace';
|
||||
|
||||
import {
|
||||
AuthException,
|
||||
AuthExceptionCode,
|
||||
@@ -57,6 +59,20 @@ const createSignInUpServiceForTests = () => {
|
||||
release: jest.fn(),
|
||||
};
|
||||
|
||||
const mockUserWorkspaceService = {
|
||||
create: jest.fn(),
|
||||
checkUserWorkspaceExists: jest.fn(),
|
||||
addUserToWorkspaceIfUserNotInWorkspace: jest.fn(),
|
||||
};
|
||||
|
||||
const mockOnboardingService = {
|
||||
setOnboardingConnectAccountPending: jest.fn(),
|
||||
setOnboardingCreateProfilePending: jest.fn(),
|
||||
setOnboardingInstallAppsPending: jest.fn(),
|
||||
setOnboardingInviteTeamPending: jest.fn(),
|
||||
createOnboardingStatusForWorkspaceMember: jest.fn(),
|
||||
};
|
||||
|
||||
const service = new SignInUpService(
|
||||
mockUserRepository as any,
|
||||
mockWorkspaceRepository as any,
|
||||
@@ -64,15 +80,8 @@ const createSignInUpServiceForTests = () => {
|
||||
validatePersonalInvitation: jest.fn(),
|
||||
invalidateWorkspaceInvitation: jest.fn(),
|
||||
} as any,
|
||||
{
|
||||
create: jest.fn(),
|
||||
checkUserWorkspaceExists: jest.fn(),
|
||||
} as any,
|
||||
{
|
||||
setOnboardingCreateProfilePending: jest.fn(),
|
||||
setOnboardingInviteTeamPending: jest.fn(),
|
||||
createOnboardingStatusForWorkspaceMember: jest.fn(),
|
||||
} as any,
|
||||
mockUserWorkspaceService as any,
|
||||
mockOnboardingService as any,
|
||||
{
|
||||
emitCustomBatchEvent: jest.fn(),
|
||||
} as any,
|
||||
@@ -121,6 +130,7 @@ const createSignInUpServiceForTests = () => {
|
||||
mockUserRepository,
|
||||
mockWorkspaceRepository,
|
||||
mockConfigurationValues,
|
||||
mockOnboardingService,
|
||||
};
|
||||
};
|
||||
|
||||
@@ -309,3 +319,34 @@ describe('SignInUpService workspace-creation policy', () => {
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('SignInUpService onboarding steps', () => {
|
||||
it('does not flag the install-apps step for a new user joining an existing workspace', async () => {
|
||||
const { service, mockOnboardingService } = createSignInUpServiceForTests();
|
||||
|
||||
await service.signInUpOnExistingWorkspace({
|
||||
workspace: {
|
||||
id: 'existing-workspace-id',
|
||||
activationStatus: WorkspaceActivationStatus.ACTIVE,
|
||||
} as any,
|
||||
userData: {
|
||||
type: 'newUserWithPicture',
|
||||
newUserWithPicture: {
|
||||
email: 'invited.user@acme.dev',
|
||||
firstName: 'Invited',
|
||||
lastName: 'User',
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
expect(
|
||||
mockOnboardingService.setOnboardingCreateProfilePending,
|
||||
).toHaveBeenCalledWith(expect.objectContaining({ value: true }), undefined);
|
||||
expect(
|
||||
mockOnboardingService.setOnboardingInstallAppsPending,
|
||||
).not.toHaveBeenCalled();
|
||||
expect(
|
||||
mockOnboardingService.setOnboardingConnectAccountPending,
|
||||
).not.toHaveBeenCalled();
|
||||
});
|
||||
});
|
||||
|
||||
@@ -332,6 +332,7 @@ export class SignInUpService {
|
||||
user,
|
||||
workspace: params.workspace,
|
||||
shouldShowConnectAccountStep: false,
|
||||
shouldShowInstallAppsStep: false,
|
||||
});
|
||||
|
||||
await this.userWorkspaceService.addUserToWorkspaceIfUserNotInWorkspace(
|
||||
@@ -364,10 +365,12 @@ export class SignInUpService {
|
||||
user,
|
||||
workspace,
|
||||
shouldShowConnectAccountStep,
|
||||
shouldShowInstallAppsStep,
|
||||
}: {
|
||||
user: Pick<UserEntity, 'id' | 'firstName' | 'lastName'>;
|
||||
workspace: WorkspaceEntity;
|
||||
shouldShowConnectAccountStep: boolean;
|
||||
shouldShowInstallAppsStep: boolean;
|
||||
},
|
||||
queryRunner?: QueryRunner,
|
||||
) {
|
||||
@@ -391,14 +394,16 @@ export class SignInUpService {
|
||||
queryRunner,
|
||||
);
|
||||
|
||||
await this.onboardingService.setOnboardingInstallAppsPending(
|
||||
{
|
||||
userId: user.id,
|
||||
workspaceId: workspace.id,
|
||||
value: true,
|
||||
},
|
||||
queryRunner,
|
||||
);
|
||||
if (shouldShowInstallAppsStep) {
|
||||
await this.onboardingService.setOnboardingInstallAppsPending(
|
||||
{
|
||||
userId: user.id,
|
||||
workspaceId: workspace.id,
|
||||
value: true,
|
||||
},
|
||||
queryRunner,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
private async saveNewUser(
|
||||
@@ -670,6 +675,7 @@ export class SignInUpService {
|
||||
user,
|
||||
workspace,
|
||||
shouldShowConnectAccountStep: true,
|
||||
shouldShowInstallAppsStep: true,
|
||||
},
|
||||
queryRunner,
|
||||
);
|
||||
|
||||
Reference in New Issue
Block a user